From a0749b141d54300efc89168fea65cb8c0c89e8d2 Mon Sep 17 00:00:00 2001 From: "Paul M. Jones" Date: Thu, 31 Dec 2020 09:41:00 -0600 Subject: [PATCH] no need for LoggedStatementInterface after all, and increase test coverage --- src/Connection.php | 10 +- src/LoggedStatement.php | 2 +- src/LoggedStatementInterface.php | 17 -- src/PersistentLoggedStatement.php | 33 +-- tests/ConnectionLocatorTest.php | 60 +++++ tests/ConnectionTest.php | 23 +- tests/LoggedStatementTest.php | 291 ------------------------ tests/PersistentLoggedStatementTest.php | 219 ++++++++++++++++++ 8 files changed, 318 insertions(+), 337 deletions(-) delete mode 100644 src/LoggedStatementInterface.php delete mode 100644 tests/LoggedStatementTest.php create mode 100644 tests/PersistentLoggedStatementTest.php diff --git a/src/Connection.php b/src/Connection.php index 0aac230..14f18d0 100644 --- a/src/Connection.php +++ b/src/Connection.php @@ -129,10 +129,16 @@ public function prepare( $sth = $this->pdo->prepare($statement, $driverOptions); if ($this->logQueries && $this->persistent) { - $sth = PersistentLoggedStatement::new($sth); + $sth = PersistentLoggedStatement::new( + $sth, + function (array $entry) : void { + $this->addLogEntry($entry); + }, + $this->newLogEntry($statement) + ); } - if ($sth instanceof LoggedStatementInterface) { + if ($sth instanceof LoggedStatement) { $sth->setLogEntry($this->newLogEntry($statement)); $sth->setQueryLogger(function (array $entry) : void { $this->addLogEntry($entry); diff --git a/src/LoggedStatement.php b/src/LoggedStatement.php index 6a58cb0..a44ab88 100644 --- a/src/LoggedStatement.php +++ b/src/LoggedStatement.php @@ -13,7 +13,7 @@ use PDO; use PDOStatement; -class LoggedStatement extends PDOStatement implements LoggedStatementInterface +class LoggedStatement extends PDOStatement { private $logEntry; diff --git a/src/LoggedStatementInterface.php b/src/LoggedStatementInterface.php deleted file mode 100644 index 8e3ad5d..0000000 --- a/src/LoggedStatementInterface.php +++ /dev/null @@ -1,17 +0,0 @@ -parent = $parent; + $sth->queryLogger = $queryLogger; + $sth->logEntry = $logEntry; return $sth; } @@ -66,7 +71,7 @@ public function bindValue( { $result = $this->parent->bindValue($parameter, $value, $dataType); - if ($result && $this->logEntry !== null) { + if ($result) { $this->logEntry['values'][$parameter] = $value; } @@ -155,24 +160,8 @@ public function nextRowset() return $this->parent->nextRowset(); } - /* Logging */ - - public function setLogEntry(array $logEntry) : void - { - $this->logEntry = $logEntry; - } - - public function setQueryLogger(callable $queryLogger) : void - { - $this->queryLogger = $queryLogger; - } - private function log($inputParameters) : void { - if ($this->queryLogger === null || $this->logEntry === null) { - return; - } - if ($inputParameters !== null) { $this->logEntry['values'] = array_replace( $this->logEntry['values'], diff --git a/tests/ConnectionLocatorTest.php b/tests/ConnectionLocatorTest.php index 427e331..a8149df 100644 --- a/tests/ConnectionLocatorTest.php +++ b/tests/ConnectionLocatorTest.php @@ -236,6 +236,66 @@ public function testQueryLogging() $this->assertSame($expect, $labels); } + public function testQueryLoggingOnExistingInstances() + { + $locator = $this->newLocator($this->read, $this->write); + $stm = "SELECT * FROM sqlite_master"; + + // create instances before logging is turned on + $locator->getDefault(); + $types = ['read', 'write']; + foreach ($types as $type) { + for ($i = 1; $i <= 3; $i ++) { + $name = $type . $i; + $locator->get(strtoupper($type), $name); + } + } + + // now turn on query logging + $locator->logQueries(true); + + // default connection + $connection = $locator->getDefault(); + $sth = $connection->perform($stm); + $this->assertInstanceOf(PDOStatement::CLASS, $sth); + + // read and write connections + $types = ['read', 'write']; + foreach ($types as $type) { + for ($i = 1; $i <= 3; $i ++) { + $name = $type . $i; + $connection = $locator->get(strtoupper($type), $name); + $sth = $connection->perform($stm); + $this->assertInstanceOf(PDOStatement::CLASS, $sth); + } + } + + $queries = $locator->getQueries(); + $this->assertCount(7, $queries); + + $labels = []; + foreach ($queries as $query) { + $labels[] = $query['connection']; + $this->assertTrue($query['start'] > 0); + $this->assertTrue($query['finish'] > $query['start']); + $this->assertTrue($query['duration'] > 0); + $this->assertTrue($query['statement'] == 'SELECT * FROM sqlite_master'); + $this->assertTrue($query['values'] === []); + $this->assertTrue($query['trace'] != ''); + } + + $expect = [ + 'DEFAULT', + 'READ:read1', + 'READ:read2', + 'READ:read3', + 'WRITE:write1', + 'WRITE:write2', + 'WRITE:write3', + ]; + $this->assertSame($expect, $labels); + } + public function testSetQueryLogger() { $entries = []; diff --git a/tests/ConnectionTest.php b/tests/ConnectionTest.php index 9fb96c9..f81bb9b 100644 --- a/tests/ConnectionTest.php +++ b/tests/ConnectionTest.php @@ -55,6 +55,14 @@ public function testFactory() $this->assertInstanceOf(Connection::CLASS, $connection); } + public function test__call() + { + $this->assertSame( + 'sqlite', + $this->connection->getAttribute(PDO::ATTR_DRIVER_NAME) + ); + } + public function testGetDriverName() { $this->assertSame('sqlite', $this->connection->getDriverName()); @@ -420,22 +428,29 @@ public function testPersistent() $persistent->logQueries(true); // when prepared from native PDO, should be PDOStatement - $sth = $persistent->getPdo()->prepare("SELECT id FROM pdotest WHERE id = 0"); + $sth = $persistent->getPdo()->prepare("SELECT id FROM pdotest WHERE id = :id"); $this->assertInstanceOf(PDOStatement::CLASS, $sth); $this->assertNotInstanceOf(PersistentLoggedStatement::CLASS, $sth); // should not log, because prepared directly from PDO - $this->assertTrue($sth->execute()); + $this->assertTrue($sth->execute(['id' => 0])); $queries = $this->connection->getQueries(); $this->assertCount(0, $queries); // when prepared from Connection, should be LoggedStatement - $sth = $persistent->prepare("SELECT id FROM pdotest WHERE id = 0"); + $sth = $persistent->prepare("SELECT id FROM pdotest WHERE id = :id"); $this->assertInstanceOf(PDOStatement::CLASS, $sth); $this->assertInstanceOf(PersistentLoggedStatement::CLASS, $sth); // should log, because prepared from Connection - $this->assertTrue($sth->execute()); + $this->assertTrue($sth->execute(['id' => 0])); + $queries = $persistent->getQueries(); + $this->assertCount(1, $queries); + + // try again without logging; should still be only 1 query + $persistent->logQueries(false); + $sth = $persistent->prepare("SELECT id FROM pdotest WHERE id = :id"); + $this->assertTrue($sth->execute(['id' => 0])); $queries = $persistent->getQueries(); $this->assertCount(1, $queries); } diff --git a/tests/LoggedStatementTest.php b/tests/LoggedStatementTest.php deleted file mode 100644 index 8444ed8..0000000 --- a/tests/LoggedStatementTest.php +++ /dev/null @@ -1,291 +0,0 @@ - 'Anna', - 2 => 'Betty', - 3 => 'Clara', - 4 => 'Donna', - 5 => 'Fiona', - 6 => 'Gertrude', - 7 => 'Hanna', - 8 => 'Ione', - 9 => 'Julia', - 10 => 'Kara', - ]; - - public function provideConnectionFactory() - { - return [ - // transient - [Connection::factory( - 'sqlite::memory:' - )], - // persistent - [Connection::factory( - 'sqlite::memory:', - '', - '', - [ - PDO::ATTR_PERSISTENT => true, - ] - )], - ]; - } - - protected function init($connectionFactory) : Connection - { - $connection = $connectionFactory(); - - $connection->exec("DROP TABLE IF EXISTS pdotest"); - - $connection->exec("CREATE TABLE pdotest ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - name VARCHAR(10) NOT NULL - )"); - - foreach ($this->data as $id => $name) { - $connection->perform( - "INSERT INTO pdotest (name) VALUES (:name)", - [ - 'name' => $name - ] - ); - } - - $connection->logQueries(); - - return $connection; - } - - /** - * @dataProvider provideConnectionFactory - */ - public function testInstantiation($connectionFactory) - { - $connection = $this->init($connectionFactory); - $sth = $connection->prepare('SELECT * FROM pdotest WHERE name = :name'); - - $expect = ($connection->getPdo()->getAttribute(PDO::ATTR_PERSISTENT)) - ? PersistentLoggedStatement::CLASS - : LoggedStatement::CLASS; - - $this->assertInstanceOf($expect, $sth); - } - - /** - * @dataProvider provideConnectionFactory - */ - public function testBindColumn($connectionFactory) - { - $connection = $this->init($connectionFactory); - $sth = $connection->prepare('SELECT * FROM pdotest WHERE name = "Anna"'); - $sth->setFetchMode(PDO::FETCH_ASSOC); - $sth->execute(); - - if ($connection->getPdo()->getAttribute(PDO::ATTR_PERSISTENT)) { - $this->expectException(BadMethodCallException::CLASS); - } - - $sth->bindColumn('name', $name); - $sth->fetch(); - $this->assertSame('Anna', $name); - } - - /** - * @dataProvider provideConnectionFactory - */ - public function testBindValue($connectionFactory) - { - $connection = $this->init($connectionFactory); - $sth = $connection->prepare('SELECT * FROM pdotest WHERE name = :name'); - $sth->setFetchMode(PDO::FETCH_ASSOC); - - $sth->bindValue('name', 'Anna'); - - $sth->execute(); - - $expect = ['id' => '1', 'name' => 'Anna']; - $actual = $sth->fetch(); - $this->assertSame($expect, $actual); - } - - /** - * @dataProvider provideConnectionFactory - */ - public function testBindParam($connectionFactory) - { - $connection = $this->init($connectionFactory); - $sth = $connection->prepare('SELECT * FROM pdotest WHERE name = :name'); - $sth->setFetchMode(PDO::FETCH_ASSOC); - - $name = 'none'; - $sth->bindParam('name', $name); - $name = 'Anna'; - - $sth->execute(); - - $expect = ['id' => '1', 'name' => 'Anna']; - $actual = $sth->fetch(); - $this->assertSame($expect, $actual); - } - - /** - * @dataProvider provideConnectionFactory - */ - public function testFetchAll($connectionFactory) - { - $connection = $this->init($connectionFactory); - $sth = $connection->prepare('SELECT * FROM pdotest WHERE id <= 3 ORDER BY id'); - $sth->setFetchMode(PDO::FETCH_ASSOC); - - $sth->execute(); - - $actual = $sth->fetchAll(); - $expect = [ - [ - 'id' => '1', - 'name' => 'Anna', - ], - [ - 'id' => '2', - 'name' => 'Betty', - ], - [ - 'id' => '3', - 'name' => 'Clara', - ] - ]; - - $this->assertSame($expect, $actual); - - $this->assertSame(2, $sth->columnCount()); - } - - /** - * @dataProvider provideConnectionFactory - */ - public function testFetchColumn($connectionFactory) - { - $connection = $this->init($connectionFactory); - $sth = $connection->prepare('SELECT * FROM pdotest ORDER BY id'); - $sth->setFetchMode(PDO::FETCH_ASSOC); - - $sth->execute(); - - $actual = $sth->fetchColumn(1); - $expect = 'Anna'; - $this->assertSame($expect, $actual); - } - - /** - * @dataProvider provideConnectionFactory - */ - public function testFetchObject($connectionFactory) - { - $connection = $this->init($connectionFactory); - $sth = $connection->prepare('SELECT * FROM pdotest ORDER BY id'); - $sth->setFetchMode(PDO::FETCH_ASSOC); - - $sth->execute(); - - $actual = $sth->fetchObject(); - $expect = (object) ['id' => '1', 'name' => 'Anna']; - $this->assertEquals($expect, $actual); - } - - /** - * @dataProvider provideConnectionFactory - */ - public function testRowCount($connectionFactory) - { - $connection = $this->init($connectionFactory); - $sth = $connection->prepare('INSERT INTO pdotest (id, name) VALUES (11, "Lara")'); - $sth->setFetchMode(PDO::FETCH_ASSOC); - - $sth->execute(); - $this->assertSame(1, $sth->rowCount()); - } - - /** - * @dataProvider provideConnectionFactory - */ - public function testGetColumnMeta($connectionFactory) - { - $connection = $this->init($connectionFactory); - $sth = $connection->prepare('SELECT name FROM pdotest ORDER BY id'); - $sth->setFetchMode(PDO::FETCH_ASSOC); - - $sth->execute(); - $actual = $sth->getColumnMeta(0); - $expect = [ - 'native_type' => 'string', - 'sqlite:decl_type' => 'VARCHAR(10)', - 'table' => 'pdotest', - 'flags' => [], - 'name' => 'name', - 'len' => -1, - 'precision' => 0, - 'pdo_type' => 2 - ]; - $this->assertEquals($expect, $actual); - } - - /** - * @dataProvider provideConnectionFactory - */ - public function testErrorCode($connectionFactory) - { - $connection = $this->init($connectionFactory); - $sth = $connection->prepare('SELECT name FROM pdotest ORDER BY id'); - $this->assertNull($sth->errorCode()); - } - - /** - * @dataProvider provideConnectionFactory - */ - public function testErrorInfo($connectionFactory) - { - $connection = $this->init($connectionFactory); - $sth = $connection->prepare('SELECT name FROM pdotest ORDER BY id'); - $expect = ['', null, null]; - $actual = $sth->errorInfo(); - $this->assertSame($expect, $actual); - } - - /** - * @dataProvider provideConnectionFactory - */ - public function testCloseCursor($connectionFactory) - { - $connection = $this->init($connectionFactory); - $sth = $connection->prepare('SELECT name FROM pdotest ORDER BY id'); - $sth->execute(); - $this->assertTrue($sth->closeCursor()); - } - - /** - * @dataProvider provideConnectionFactory - */ - public function testDebugDumpParams($connectionFactory) - { - $connection = $this->init($connectionFactory); - $sth = $connection->prepare('SELECT name FROM pdotest ORDER BY id'); - $sth->execute(); - - ob_start(); - $sth->debugDumpParams(); - $actual = ob_get_clean(); - - $expect = "SQL: [36] SELECT name FROM pdotest ORDER BY id" . PHP_EOL - . "Params: 0" . PHP_EOL; - $this->assertSame($expect, $actual); - } -} diff --git a/tests/PersistentLoggedStatementTest.php b/tests/PersistentLoggedStatementTest.php new file mode 100644 index 0000000..6904f82 --- /dev/null +++ b/tests/PersistentLoggedStatementTest.php @@ -0,0 +1,219 @@ + 'Anna', + 2 => 'Betty', + 3 => 'Clara', + 4 => 'Donna', + 5 => 'Fiona', + 6 => 'Gertrude', + 7 => 'Hanna', + 8 => 'Ione', + 9 => 'Julia', + 10 => 'Kara', + ]; + + protected function setUp() + { + $this->connection = Connection::new( + 'sqlite::memory:', + '', + '', + [ + PDO::ATTR_PERSISTENT => true, + ] + ); + + $this->connection->exec("DROP TABLE IF EXISTS pdotest"); + + $this->connection->exec("CREATE TABLE pdotest ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name VARCHAR(10) NOT NULL + )"); + + foreach ($this->data as $id => $name) { + $this->connection->perform( + "INSERT INTO pdotest (name) VALUES (:name)", + [ + 'name' => $name + ] + ); + } + + $this->connection->logQueries(true); + } + + public function testInstantiation() + { + $sth = $this->connection->prepare('SELECT * FROM pdotest WHERE name = :name'); + + $expect = ($this->connection->getPdo()->getAttribute(PDO::ATTR_PERSISTENT)) + ? PersistentLoggedStatement::CLASS + : LoggedStatement::CLASS; + + $this->assertInstanceOf($expect, $sth); + } + + public function testBindColumn_badMethod() + { + $sth = $this->connection->prepare('SELECT * FROM pdotest WHERE name = "Anna"'); + $sth->setFetchMode(PDO::FETCH_ASSOC); + $sth->execute(); + $this->expectException(BadMethodCallException::CLASS); + $sth->bindColumn('name', $name); + } + + public function testBindValue() + { + $sth = $this->connection->prepare('SELECT * FROM pdotest WHERE name = :name'); + $sth->setFetchMode(PDO::FETCH_ASSOC); + + $sth->bindValue('name', 'Anna'); + + $sth->execute(); + + $expect = ['id' => '1', 'name' => 'Anna']; + $actual = $sth->fetch(); + $this->assertSame($expect, $actual); + } + + public function testBindParam() + { + $sth = $this->connection->prepare('SELECT * FROM pdotest WHERE name = :name'); + $sth->setFetchMode(PDO::FETCH_ASSOC); + + $name = 'none'; + $sth->bindParam('name', $name); + $name = 'Anna'; + + $sth->execute(); + + $expect = ['id' => '1', 'name' => 'Anna']; + $actual = $sth->fetch(); + $this->assertSame($expect, $actual); + } + + public function testFetchAll() + { + $sth = $this->connection->prepare('SELECT * FROM pdotest WHERE id <= 3 ORDER BY id'); + $sth->setFetchMode(PDO::FETCH_ASSOC); + + $sth->execute(); + + $actual = $sth->fetchAll(); + $expect = [ + [ + 'id' => '1', + 'name' => 'Anna', + ], + [ + 'id' => '2', + 'name' => 'Betty', + ], + [ + 'id' => '3', + 'name' => 'Clara', + ] + ]; + + $this->assertSame($expect, $actual); + + $this->assertSame(2, $sth->columnCount()); + } + + public function testFetchColumn() + { + $sth = $this->connection->prepare('SELECT * FROM pdotest ORDER BY id'); + $sth->setFetchMode(PDO::FETCH_ASSOC); + + $sth->execute(); + + $actual = $sth->fetchColumn(1); + $expect = 'Anna'; + $this->assertSame($expect, $actual); + } + + public function testFetchObject() + { + $sth = $this->connection->prepare('SELECT * FROM pdotest ORDER BY id'); + $sth->setFetchMode(PDO::FETCH_ASSOC); + + $sth->execute(); + + $actual = $sth->fetchObject(); + $expect = (object) ['id' => '1', 'name' => 'Anna']; + $this->assertEquals($expect, $actual); + } + + public function testRowCount() + { + $sth = $this->connection->prepare('INSERT INTO pdotest (id, name) VALUES (11, "Lara")'); + $sth->setFetchMode(PDO::FETCH_ASSOC); + + $sth->execute(); + $this->assertSame(1, $sth->rowCount()); + } + + public function testGetColumnMeta() + { + $sth = $this->connection->prepare('SELECT name FROM pdotest ORDER BY id'); + $sth->setFetchMode(PDO::FETCH_ASSOC); + + $sth->execute(); + $actual = $sth->getColumnMeta(0); + unset($actual['len']); + $expect = [ + 'native_type' => 'string', + 'sqlite:decl_type' => 'VARCHAR(10)', + 'table' => 'pdotest', + 'flags' => [], + 'name' => 'name', + 'precision' => 0, + 'pdo_type' => 2 + ]; + $this->assertEquals($expect, $actual); + } + + public function testErrorCode() + { + $sth = $this->connection->prepare('SELECT name FROM pdotest ORDER BY id'); + $this->assertNull($sth->errorCode()); + } + + public function testErrorInfo() + { + $sth = $this->connection->prepare('SELECT name FROM pdotest ORDER BY id'); + $expect = ['', null, null]; + $actual = $sth->errorInfo(); + $this->assertSame($expect, $actual); + } + + public function testCloseCursor() + { + $sth = $this->connection->prepare('SELECT name FROM pdotest ORDER BY id'); + $sth->execute(); + $this->assertTrue($sth->closeCursor()); + } + + public function testDebugDumpParams() + { + $sth = $this->connection->prepare('SELECT name FROM pdotest ORDER BY id'); + $sth->execute(); + + ob_start(); + $sth->debugDumpParams(); + $actual = ob_get_clean(); + + $expect = "SQL: [36] SELECT name FROM pdotest ORDER BY id" . PHP_EOL + . "Params: 0" . PHP_EOL; + $this->assertSame($expect, $actual); + } +}