diff --git a/mailpoet/lib/Doctrine/WPDB/Connection.php b/mailpoet/lib/Doctrine/WPDB/Connection.php index 11ac2eb534..a7e8e83074 100644 --- a/mailpoet/lib/Doctrine/WPDB/Connection.php +++ b/mailpoet/lib/Doctrine/WPDB/Connection.php @@ -25,7 +25,10 @@ class Connection implements ServerInfoAwareConnection { } public function query(string $sql): Result { - // TODO: Implement query() method. + global $wpdb; + $value = $this->runQuery($sql); + $result = $wpdb->last_result; + return new Result($result, is_int($value) ? $value : 0); } public function exec(string $sql): int { diff --git a/mailpoet/lib/Doctrine/WPDB/Result.php b/mailpoet/lib/Doctrine/WPDB/Result.php index 17e1d773f7..e11ec1f6ae 100644 --- a/mailpoet/lib/Doctrine/WPDB/Result.php +++ b/mailpoet/lib/Doctrine/WPDB/Result.php @@ -4,40 +4,69 @@ namespace MailPoet\Doctrine\WPDB; use MailPoetVendor\Doctrine\DBAL\Driver\Result as ResultInterface; +/** + * WPDB fetches all results from the underlying database driver, + * so we need to implement the result methods on in-memory data. + */ class Result implements ResultInterface { + /** @var array[] */ + private array $result = []; + private int $rowCount; + private int $cursor = 0; + + public function __construct( + array $result, + int $rowCount + ) { + foreach ($result as $value) { + $this->result[] = (array)$value; + } + $this->rowCount = $rowCount; + } + public function fetchNumeric() { - // TODO: Implement fetchNumeric() method. + $value = $this->result[$this->cursor++] ?? null; + return $value === null ? false : array_values($value); } public function fetchAssociative() { - // TODO: Implement fetchAssociative() method. + return $this->result[$this->cursor++] ?? false; } public function fetchOne() { - // TODO: Implement fetchOne() method. + $value = $this->result[$this->cursor++] ?? null; + return $value === null ? false : reset($value); } public function fetchAllNumeric(): array { - // TODO: Implement fetchAllNumeric() method. + $result = []; + foreach ($this->result as $value) { + $result[] = array_values($value); + } + return $result; } public function fetchAllAssociative(): array { - // TODO: Implement fetchAllAssociative() method. + return $this->result; } public function fetchFirstColumn(): array { - // TODO: Implement fetchFirstColumn() method. + $result = []; + foreach ($this->result as $value) { + $result[] = reset($value); + } + return $result; } public function rowCount(): int { - // TODO: Implement rowCount() method. + return $this->rowCount; } public function columnCount(): int { - // TODO: Implement columnCount() method. + return count($this->result[0] ?? []); } public function free(): void { - // TODO: Implement free() method. + $this->cursor = 0; } } diff --git a/mailpoet/tests/integration/Doctrine/WPDB/ConnectionTest.php b/mailpoet/tests/integration/Doctrine/WPDB/ConnectionTest.php index 9cfe8502a7..70ede27985 100644 --- a/mailpoet/tests/integration/Doctrine/WPDB/ConnectionTest.php +++ b/mailpoet/tests/integration/Doctrine/WPDB/ConnectionTest.php @@ -3,6 +3,7 @@ namespace MailPoet\Test\Doctrine\WPDB; use MailPoet\Doctrine\WPDB\Connection; +use MailPoet\Doctrine\WPDB\Result; use MailPoetTest; use mysqli; @@ -24,6 +25,30 @@ class ConnectionTest extends MailPoetTest { $this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); } + public function testQuery(): void { + $connection = new Connection(); + + // select + $result = $connection->query('SELECT 123'); + $this->assertInstanceOf(Result::class, $result); + $this->assertSame('123', $result->fetchOne()); + + // insert + $result = $connection->query(sprintf("INSERT INTO %s (value) VALUES ('test')", self::TEST_TABLE_NAME)); + $this->assertInstanceOf(Result::class, $result); + $this->assertFalse($result->fetchOne()); + + // update + $result = $connection->query(sprintf("UPDATE %s SET value = 'updated' WHERE id = %d", self::TEST_TABLE_NAME, $connection->lastInsertId())); + $this->assertInstanceOf(Result::class, $result); + $this->assertFalse($result->fetchOne()); + + // delete + $result = $connection->query(sprintf("DELETE FROM %s WHERE id = %d", self::TEST_TABLE_NAME, $connection->lastInsertId())); + $this->assertInstanceOf(Result::class, $result); + $this->assertFalse($result->fetchOne()); + } + public function testExec(): void { $connection = new Connection(); diff --git a/mailpoet/tests/integration/Doctrine/WPDB/ResultTest.php b/mailpoet/tests/integration/Doctrine/WPDB/ResultTest.php new file mode 100644 index 0000000000..daf9162203 --- /dev/null +++ b/mailpoet/tests/integration/Doctrine/WPDB/ResultTest.php @@ -0,0 +1,165 @@ +exec(sprintf('DROP TABLE IF EXISTS %s', self::TEST_TABLE_NAME)); + $connection->exec(sprintf(' + CREATE TABLE %s ( + id int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY, + value varchar(255) NOT NULL) + ', self::TEST_TABLE_NAME)); + } + + public function testInsert(): void { + $connection = new Connection(); + $result = $connection->query(sprintf("INSERT INTO %s (value) VALUES ('test')", self::TEST_TABLE_NAME)); + $this->assertInstanceOf(Result::class, $result); + $this->assertSame(1, $result->rowCount()); + $this->assertSame(0, $result->columnCount()); + $this->assertSame([], $result->fetchAllAssociative()); + } + + public function testUpdate(): void { + $connection = new Connection(); + $connection->query(sprintf("INSERT INTO %s (value) VALUES ('test')", self::TEST_TABLE_NAME)); + $result = $connection->query(sprintf("UPDATE %s SET value = 'updated' WHERE id = %d", self::TEST_TABLE_NAME, $connection->lastInsertId())); + $this->assertInstanceOf(Result::class, $result); + $this->assertSame(1, $result->rowCount()); + $this->assertSame(0, $result->columnCount()); + $this->assertSame([], $result->fetchAllAssociative()); + } + + public function testFetchNumeric(): void { + $connection = new Connection(); + $connection->query(sprintf("INSERT INTO %s (value) VALUES ('aaa')", self::TEST_TABLE_NAME)); + $connection->query(sprintf("INSERT INTO %s (value) VALUES ('bbb')", self::TEST_TABLE_NAME)); + $connection->query(sprintf("INSERT INTO %s (value) VALUES ('ccc')", self::TEST_TABLE_NAME)); + + $result = $connection->query(sprintf("SELECT * FROM %s", self::TEST_TABLE_NAME)); + $this->assertInstanceOf(Result::class, $result); + $this->assertSame(['1', 'aaa'], $result->fetchNumeric()); + $this->assertSame(['2', 'bbb'], $result->fetchNumeric()); + $this->assertSame(['3', 'ccc'], $result->fetchNumeric()); + $this->assertFalse($result->fetchNumeric()); + } + + public function testFetchAssociative(): void { + $connection = new Connection(); + $connection->query(sprintf("INSERT INTO %s (value) VALUES ('aaa')", self::TEST_TABLE_NAME)); + $connection->query(sprintf("INSERT INTO %s (value) VALUES ('bbb')", self::TEST_TABLE_NAME)); + $connection->query(sprintf("INSERT INTO %s (value) VALUES ('ccc')", self::TEST_TABLE_NAME)); + + $result = $connection->query(sprintf("SELECT * FROM %s", self::TEST_TABLE_NAME)); + $this->assertInstanceOf(Result::class, $result); + $this->assertSame(['id' => '1', 'value' => 'aaa'], $result->fetchAssociative()); + $this->assertSame(['id' => '2', 'value' => 'bbb'], $result->fetchAssociative()); + $this->assertSame(['id' => '3', 'value' => 'ccc'], $result->fetchAssociative()); + $this->assertFalse($result->fetchAssociative()); + } + + public function testFetchOne(): void { + $connection = new Connection(); + $connection->query(sprintf("INSERT INTO %s (value) VALUES ('aaa')", self::TEST_TABLE_NAME)); + $connection->query(sprintf("INSERT INTO %s (value) VALUES ('bbb')", self::TEST_TABLE_NAME)); + $connection->query(sprintf("INSERT INTO %s (value) VALUES ('ccc')", self::TEST_TABLE_NAME)); + + $result = $connection->query(sprintf("SELECT * FROM %s", self::TEST_TABLE_NAME)); + $this->assertInstanceOf(Result::class, $result); + $this->assertSame('1', $result->fetchOne()); + $this->assertSame('2', $result->fetchOne()); + $this->assertSame('3', $result->fetchOne()); + $this->assertFalse($result->fetchOne()); + } + + public function testFetchAllNumeric(): void { + $connection = new Connection(); + $connection->query(sprintf("INSERT INTO %s (value) VALUES ('aaa')", self::TEST_TABLE_NAME)); + $connection->query(sprintf("INSERT INTO %s (value) VALUES ('bbb')", self::TEST_TABLE_NAME)); + $connection->query(sprintf("INSERT INTO %s (value) VALUES ('ccc')", self::TEST_TABLE_NAME)); + + $result = $connection->query(sprintf("SELECT * FROM %s", self::TEST_TABLE_NAME)); + $this->assertInstanceOf(Result::class, $result); + $this->assertSame([['1', 'aaa'], ['2', 'bbb'], ['3', 'ccc']], $result->fetchAllNumeric()); + } + + public function testFetchAllAssociative(): void { + $connection = new Connection(); + $connection->query(sprintf("INSERT INTO %s (value) VALUES ('aaa')", self::TEST_TABLE_NAME)); + $connection->query(sprintf("INSERT INTO %s (value) VALUES ('bbb')", self::TEST_TABLE_NAME)); + $connection->query(sprintf("INSERT INTO %s (value) VALUES ('ccc')", self::TEST_TABLE_NAME)); + + $result = $connection->query(sprintf("SELECT * FROM %s", self::TEST_TABLE_NAME)); + $this->assertInstanceOf(Result::class, $result); + $this->assertSame([ + ['id' => '1', 'value' => 'aaa'], + ['id' => '2', 'value' => 'bbb'], + ['id' => '3', 'value' => 'ccc'], + ], $result->fetchAllAssociative()); + } + + public function testFetchFirstColumn(): void { + $connection = new Connection(); + $connection->query(sprintf("INSERT INTO %s (value) VALUES ('aaa')", self::TEST_TABLE_NAME)); + $connection->query(sprintf("INSERT INTO %s (value) VALUES ('bbb')", self::TEST_TABLE_NAME)); + $connection->query(sprintf("INSERT INTO %s (value) VALUES ('ccc')", self::TEST_TABLE_NAME)); + + $result = $connection->query(sprintf("SELECT * FROM %s", self::TEST_TABLE_NAME)); + $this->assertInstanceOf(Result::class, $result); + $this->assertSame(['1', '2', '3'], $result->fetchFirstColumn()); + } + + public function testRowCount(): void { + $connection = new Connection(); + $connection->query(sprintf("INSERT INTO %s (value) VALUES ('aaa')", self::TEST_TABLE_NAME)); + $connection->query(sprintf("INSERT INTO %s (value) VALUES ('bbb')", self::TEST_TABLE_NAME)); + $connection->query(sprintf("INSERT INTO %s (value) VALUES ('ccc')", self::TEST_TABLE_NAME)); + + $result = $connection->query(sprintf("SELECT * FROM %s", self::TEST_TABLE_NAME)); + $this->assertInstanceOf(Result::class, $result); + $this->assertSame(3, $result->rowCount()); + } + + public function testColumnCount(): void { + $connection = new Connection(); + $connection->query(sprintf("INSERT INTO %s (value) VALUES ('aaa')", self::TEST_TABLE_NAME)); + $connection->query(sprintf("INSERT INTO %s (value) VALUES ('bbb')", self::TEST_TABLE_NAME)); + $connection->query(sprintf("INSERT INTO %s (value) VALUES ('ccc')", self::TEST_TABLE_NAME)); + + $result = $connection->query(sprintf("SELECT * FROM %s", self::TEST_TABLE_NAME)); + $this->assertInstanceOf(Result::class, $result); + $this->assertSame(2, $result->columnCount()); + } + + public function testFree(): void { + $connection = new Connection(); + $connection->query(sprintf("INSERT INTO %s (value) VALUES ('aaa')", self::TEST_TABLE_NAME)); + $connection->query(sprintf("INSERT INTO %s (value) VALUES ('bbb')", self::TEST_TABLE_NAME)); + $connection->query(sprintf("INSERT INTO %s (value) VALUES ('ccc')", self::TEST_TABLE_NAME)); + + $result = $connection->query(sprintf("SELECT * FROM %s", self::TEST_TABLE_NAME)); + $this->assertInstanceOf(Result::class, $result); + $this->assertSame('1', $result->fetchOne()); + $this->assertSame('2', $result->fetchOne()); + $this->assertSame('3', $result->fetchOne()); + $this->assertFalse($result->fetchOne()); + + $result->free(); + $this->assertSame('1', $result->fetchOne()); + } + + public function _after() { + parent::_after(); + $connection = new Connection(); + $connection->exec(sprintf('DROP TABLE IF EXISTS %s', self::TEST_TABLE_NAME)); + } +} diff --git a/mailpoet/tests/unit/Doctrine/WPDB/ResultTest.php b/mailpoet/tests/unit/Doctrine/WPDB/ResultTest.php new file mode 100644 index 0000000000..f6f73035cd --- /dev/null +++ b/mailpoet/tests/unit/Doctrine/WPDB/ResultTest.php @@ -0,0 +1,107 @@ +data = [ + (object)['id' => '1', 'value' => 'aaa'], + (object)['id' => '2', 'value' => 'bbb'], + (object)['id' => '3', 'value' => 'ccc'], + ]; + } + + public function testFetchNumeric(): void { + $result = new Result($this->data, 2); + $this->assertSame(['1', 'aaa'], $result->fetchNumeric()); + $this->assertSame(['2', 'bbb'], $result->fetchNumeric()); + $this->assertSame(['3', 'ccc'], $result->fetchNumeric()); + $this->assertSame(false, $result->fetchNumeric()); + + $result = new Result([], 0); + $this->assertSame(false, $result->fetchNumeric()); + } + + public function testFetchAssociative(): void { + $result = new Result($this->data, 2); + $this->assertSame(['id' => '1', 'value' => 'aaa'], $result->fetchAssociative()); + $this->assertSame(['id' => '2', 'value' => 'bbb'], $result->fetchAssociative()); + $this->assertSame(['id' => '3', 'value' => 'ccc'], $result->fetchAssociative()); + $this->assertSame(false, $result->fetchAssociative()); + + $result = new Result([], 0); + $this->assertSame(false, $result->fetchAssociative()); + } + + public function testFetchOne(): void { + $result = new Result($this->data, 2); + $this->assertSame('1', $result->fetchOne()); + $this->assertSame('2', $result->fetchOne()); + $this->assertSame('3', $result->fetchOne()); + $this->assertSame(false, $result->fetchOne()); + + $result = new Result([], 0); + $this->assertSame(false, $result->fetchOne()); + } + + public function testFetchAllNumeric(): void { + $result = new Result($this->data, 2); + $this->assertSame([['1', 'aaa'], ['2', 'bbb'], ['3', 'ccc']], $result->fetchAllNumeric()); + + $result = new Result([], 0); + $this->assertSame([], $result->fetchAllNumeric()); + } + + public function testFetchAllAssociative(): void { + $result = new Result($this->data, 2); + $this->assertSame([ + ['id' => '1', 'value' => 'aaa'], + ['id' => '2', 'value' => 'bbb'], + ['id' => '3', 'value' => 'ccc'], + ], $result->fetchAllAssociative()); + + $result = new Result([], 0); + $this->assertSame([], $result->fetchAllAssociative()); + } + + public function testFetchFirstColumn(): void { + $result = new Result($this->data, 2); + $this->assertSame(['1', '2', '3'], $result->fetchFirstColumn()); + + $result = new Result([], 0); + $this->assertSame([], $result->fetchFirstColumn()); + } + + public function testRowCount(): void { + $result = new Result($this->data, 2); + $this->assertSame(2, $result->rowCount()); + + $result = new Result([], 0); + $this->assertSame(0, $result->rowCount()); + } + + public function testColumnCount(): void { + $result = new Result($this->data, 2); + $this->assertSame(2, $result->columnCount()); + + $result = new Result([], 0); + $this->assertSame(0, $result->columnCount()); + } + + public function testFree(): void { + $result = new Result($this->data, 2); + $this->assertSame('1', $result->fetchOne()); + $this->assertSame('2', $result->fetchOne()); + $this->assertSame('3', $result->fetchOne()); + $this->assertSame(false, $result->fetchOne()); + + $result->free(); + $this->assertSame('1', $result->fetchOne()); + } +}