Merge pull request #18023 from MauricioFauth/dbal-statement
Create `Dbal/Statement` interface
This commit is contained in:
commit
6ca4c24848
@ -12,6 +12,7 @@ use PhpMyAdmin\Dbal\DbalInterface;
|
||||
use PhpMyAdmin\Dbal\DbiExtension;
|
||||
use PhpMyAdmin\Dbal\DbiMysqli;
|
||||
use PhpMyAdmin\Dbal\ResultInterface;
|
||||
use PhpMyAdmin\Dbal\Statement;
|
||||
use PhpMyAdmin\Dbal\Warning;
|
||||
use PhpMyAdmin\Html\Generator;
|
||||
use PhpMyAdmin\Query\Cache;
|
||||
@ -2092,10 +2093,8 @@ class DatabaseInterface implements DbalInterface
|
||||
*
|
||||
* @param string $query The query, as a string.
|
||||
* @psalm-param ConnectionType $connectionType
|
||||
*
|
||||
* @return object|false A statement object or false.
|
||||
*/
|
||||
public function prepare(string $query, int $connectionType = Connection::TYPE_USER)
|
||||
public function prepare(string $query, int $connectionType = Connection::TYPE_USER): ?Statement
|
||||
{
|
||||
return $this->extension->prepare($this->connections[$connectionType], $query);
|
||||
}
|
||||
|
||||
@ -679,8 +679,6 @@ interface DbalInterface
|
||||
*
|
||||
* @param string $query The query, as a string.
|
||||
* @psalm-param ConnectionType $connectionType
|
||||
*
|
||||
* @return object|false A statement object or false.
|
||||
*/
|
||||
public function prepare(string $query, int $connectionType = Connection::TYPE_USER);
|
||||
public function prepare(string $query, int $connectionType = Connection::TYPE_USER): ?Statement;
|
||||
}
|
||||
|
||||
@ -109,10 +109,8 @@ interface DbiExtension
|
||||
* Prepare an SQL statement for execution.
|
||||
*
|
||||
* @param string $query The query, as a string.
|
||||
*
|
||||
* @return object|false A statement object or false.
|
||||
*/
|
||||
public function prepare(Connection $connection, string $query);
|
||||
public function prepare(Connection $connection, string $query): ?Statement;
|
||||
|
||||
/**
|
||||
* Returns the number of warnings from the last query.
|
||||
|
||||
@ -9,7 +9,6 @@ namespace PhpMyAdmin\Dbal;
|
||||
|
||||
use mysqli;
|
||||
use mysqli_sql_exception;
|
||||
use mysqli_stmt;
|
||||
use PhpMyAdmin\Config\Settings\Server;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\Query\Utilities;
|
||||
@ -346,15 +345,17 @@ class DbiMysqli implements DbiExtension
|
||||
* Prepare an SQL statement for execution.
|
||||
*
|
||||
* @param string $query The query, as a string.
|
||||
*
|
||||
* @return mysqli_stmt|false A statement object or false.
|
||||
*/
|
||||
public function prepare(Connection $connection, string $query)
|
||||
public function prepare(Connection $connection, string $query): ?Statement
|
||||
{
|
||||
/** @var mysqli $mysqli */
|
||||
$mysqli = $connection->connection;
|
||||
$statement = $mysqli->prepare($query);
|
||||
if ($statement === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $mysqli->prepare($query);
|
||||
return new MysqliStatement($statement);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
59
libraries/classes/Dbal/MysqliStatement.php
Normal file
59
libraries/classes/Dbal/MysqliStatement.php
Normal file
@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Dbal;
|
||||
|
||||
use mysqli_stmt;
|
||||
|
||||
use function count;
|
||||
use function str_repeat;
|
||||
|
||||
use const PHP_VERSION_ID;
|
||||
|
||||
final class MysqliStatement implements Statement
|
||||
{
|
||||
/** @var mysqli_stmt */
|
||||
private $statement;
|
||||
|
||||
public function __construct(mysqli_stmt $statement)
|
||||
{
|
||||
$this->statement = $statement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a prepared statement.
|
||||
*
|
||||
* @param list<string> $params
|
||||
*/
|
||||
public function execute(array $params): bool
|
||||
{
|
||||
$paramCount = $this->statement->param_count;
|
||||
if (count($params) !== $paramCount) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (PHP_VERSION_ID >= 80100) {
|
||||
/**
|
||||
* @psalm-suppress TooManyArguments
|
||||
* @phpstan-ignore-next-line
|
||||
*/
|
||||
return $this->statement->execute($params);
|
||||
}
|
||||
|
||||
$types = str_repeat('s', $paramCount);
|
||||
if (! $this->statement->bind_param($types, ...$params)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->statement->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a result set from a prepared statement.
|
||||
*/
|
||||
public function getResult(): ResultInterface
|
||||
{
|
||||
return new MysqliResult($this->statement->get_result());
|
||||
}
|
||||
}
|
||||
20
libraries/classes/Dbal/Statement.php
Normal file
20
libraries/classes/Dbal/Statement.php
Normal file
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Dbal;
|
||||
|
||||
interface Statement
|
||||
{
|
||||
/**
|
||||
* Executes a prepared statement.
|
||||
*
|
||||
* @param list<string> $params
|
||||
*/
|
||||
public function execute(array $params): bool;
|
||||
|
||||
/**
|
||||
* Gets a result set from a prepared statement.
|
||||
*/
|
||||
public function getResult(): ResultInterface;
|
||||
}
|
||||
@ -7,7 +7,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Server;
|
||||
|
||||
use mysqli_stmt;
|
||||
use PhpMyAdmin\ConfigStorage\Features\ConfigurableMenusFeature;
|
||||
use PhpMyAdmin\ConfigStorage\Relation;
|
||||
use PhpMyAdmin\ConfigStorage\RelationCleanup;
|
||||
@ -15,7 +14,6 @@ use PhpMyAdmin\Database\Routines;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\Dbal\Connection;
|
||||
use PhpMyAdmin\Dbal\DatabaseName;
|
||||
use PhpMyAdmin\Dbal\MysqliResult;
|
||||
use PhpMyAdmin\Dbal\ResultInterface;
|
||||
use PhpMyAdmin\Dbal\TableName;
|
||||
use PhpMyAdmin\Html\Generator;
|
||||
@ -1425,13 +1423,12 @@ class Privileges
|
||||
NOT (`Table_priv` = \'\' AND Column_priv = \'\')
|
||||
ORDER BY `User` ASC, `Host` ASC, `Db` ASC, `Table_priv` ASC;
|
||||
';
|
||||
/** @var mysqli_stmt|false $statement */
|
||||
$statement = $this->dbi->prepare($query);
|
||||
if ($statement === false || ! $statement->bind_param('ss', $db, $table) || ! $statement->execute()) {
|
||||
if ($statement === null || ! $statement->execute([$db->getName(), $table->getName()])) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$result = new MysqliResult($statement->get_result());
|
||||
$result = $statement->getResult();
|
||||
|
||||
return $result->fetchAllAssoc();
|
||||
}
|
||||
@ -3769,13 +3766,12 @@ class Privileges
|
||||
private function getUserPrivileges(string $user, string $host, bool $hasAccountLocking): ?array
|
||||
{
|
||||
$query = 'SELECT * FROM `mysql`.`user` WHERE `User` = ? AND `Host` = ?;';
|
||||
/** @var mysqli_stmt|false $statement */
|
||||
$statement = $this->dbi->prepare($query);
|
||||
if ($statement === false || ! $statement->bind_param('ss', $user, $host) || ! $statement->execute()) {
|
||||
if ($statement === null || ! $statement->execute([$user, $host])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$result = new MysqliResult($statement->get_result());
|
||||
$result = $statement->getResult();
|
||||
/** @var array<string, string|null>|null $userPrivileges */
|
||||
$userPrivileges = $result->fetchAssoc();
|
||||
if ($userPrivileges === []) {
|
||||
@ -3789,13 +3785,12 @@ class Privileges
|
||||
$userPrivileges['account_locked'] = 'N';
|
||||
|
||||
$query = 'SELECT * FROM `mysql`.`global_priv` WHERE `User` = ? AND `Host` = ?;';
|
||||
/** @var mysqli_stmt|false $statement */
|
||||
$statement = $this->dbi->prepare($query);
|
||||
if ($statement === false || ! $statement->bind_param('ss', $user, $host) || ! $statement->execute()) {
|
||||
if ($statement === null || ! $statement->execute([$user, $host])) {
|
||||
return $userPrivileges;
|
||||
}
|
||||
|
||||
$result = new MysqliResult($statement->get_result());
|
||||
$result = $statement->getResult();
|
||||
/** @var array<string, string|null>|null $globalPrivileges */
|
||||
$globalPrivileges = $result->fetchAssoc();
|
||||
if ($globalPrivileges === []) {
|
||||
|
||||
@ -4,12 +4,12 @@ declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Tests;
|
||||
|
||||
use mysqli_stmt;
|
||||
use PhpMyAdmin\ConfigStorage\Relation;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\Dbal\Connection;
|
||||
use PhpMyAdmin\Dbal\DbiExtension;
|
||||
use PhpMyAdmin\Dbal\ResultInterface;
|
||||
use PhpMyAdmin\Dbal\Statement;
|
||||
use PhpMyAdmin\LanguageManager;
|
||||
use PhpMyAdmin\Query\Utilities;
|
||||
use PhpMyAdmin\SqlParser\Context;
|
||||
@ -826,7 +826,7 @@ class DatabaseInterfaceTest extends AbstractTestCase
|
||||
public function testPrepare(): void
|
||||
{
|
||||
$query = 'SELECT * FROM `mysql`.`user` WHERE `User` = ? AND `Host` = ?;';
|
||||
$stmtStub = $this->createStub(mysqli_stmt::class);
|
||||
$stmtStub = $this->createStub(Statement::class);
|
||||
$dummyDbi = $this->createMock(DbiExtension::class);
|
||||
$dummyDbi->expects($this->once())->method('prepare')
|
||||
->with($this->isType('object'), $this->equalTo($query))
|
||||
|
||||
26
test/classes/Dbal/MysqliStatementTest.php
Normal file
26
test/classes/Dbal/MysqliStatementTest.php
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Tests\Dbal;
|
||||
|
||||
use mysqli_stmt;
|
||||
use PhpMyAdmin\Dbal\MysqliResult;
|
||||
use PhpMyAdmin\Dbal\MysqliStatement;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
/**
|
||||
* @covers \PhpMyAdmin\Dbal\MysqliStatement
|
||||
* @covers \PhpMyAdmin\Dbal\MysqliResult
|
||||
*/
|
||||
class MysqliStatementTest extends TestCase
|
||||
{
|
||||
public function testGetResult(): void
|
||||
{
|
||||
$mysqliStmt = $this->createMock(mysqli_stmt::class);
|
||||
$mysqliStmt->expects($this->once())->method('get_result')->willReturn(false);
|
||||
$statement = new MysqliStatement($mysqliStmt);
|
||||
$result = $statement->getResult();
|
||||
$this->assertInstanceOf(MysqliResult::class, $result);
|
||||
}
|
||||
}
|
||||
@ -4,12 +4,12 @@ declare(strict_types=1);
|
||||
|
||||
namespace PhpMyAdmin\Tests\Server;
|
||||
|
||||
use mysqli_result;
|
||||
use mysqli_stmt;
|
||||
use PhpMyAdmin\ConfigStorage\Relation;
|
||||
use PhpMyAdmin\ConfigStorage\RelationCleanup;
|
||||
use PhpMyAdmin\ConfigStorage\RelationParameters;
|
||||
use PhpMyAdmin\DatabaseInterface;
|
||||
use PhpMyAdmin\Dbal\ResultInterface;
|
||||
use PhpMyAdmin\Dbal\Statement;
|
||||
use PhpMyAdmin\Html\Generator;
|
||||
use PhpMyAdmin\Message;
|
||||
use PhpMyAdmin\Server\Plugins;
|
||||
@ -1906,13 +1906,10 @@ class PrivilegesTest extends AbstractTestCase
|
||||
|
||||
public function testGetUserPrivileges(): void
|
||||
{
|
||||
$mysqliResultStub = $this->createMock(mysqli_result::class);
|
||||
$mysqliStmtStub = $this->createMock(mysqli_stmt::class);
|
||||
$mysqliStmtStub->expects($this->exactly(2))->method('bind_param')->willReturn(true);
|
||||
$mysqliResultStub = $this->createMock(ResultInterface::class);
|
||||
$mysqliStmtStub = $this->createMock(Statement::class);
|
||||
$mysqliStmtStub->expects($this->exactly(2))->method('execute')->willReturn(true);
|
||||
$mysqliStmtStub->expects($this->exactly(2))
|
||||
->method('get_result')
|
||||
->willReturn($mysqliResultStub);
|
||||
$mysqliStmtStub->expects($this->exactly(2))->method('getResult')->willReturn($mysqliResultStub);
|
||||
|
||||
$dbi = $this->createMock(DatabaseInterface::class);
|
||||
$dbi->expects($this->once())->method('isMariaDB')->willReturn(true);
|
||||
@ -1924,7 +1921,7 @@ class PrivilegesTest extends AbstractTestCase
|
||||
)
|
||||
->willReturn($mysqliStmtStub);
|
||||
$mysqliResultStub->expects($this->exactly(2))
|
||||
->method('fetch_assoc')
|
||||
->method('fetchAssoc')
|
||||
->willReturnOnConsecutiveCalls(
|
||||
['Host' => 'test.host', 'User' => 'test.user'],
|
||||
['Host' => 'test.host', 'User' => 'test.user', 'Priv' => '{"account_locked":true}']
|
||||
|
||||
@ -16,6 +16,7 @@ use PhpMyAdmin\Dbal\Connection;
|
||||
use PhpMyAdmin\Dbal\DatabaseName;
|
||||
use PhpMyAdmin\Dbal\DbiExtension;
|
||||
use PhpMyAdmin\Dbal\ResultInterface;
|
||||
use PhpMyAdmin\Dbal\Statement;
|
||||
use PhpMyAdmin\FieldMetadata;
|
||||
use PHPUnit\Framework\Assert;
|
||||
use stdClass;
|
||||
@ -495,14 +496,9 @@ class DbiDummy implements DbiExtension
|
||||
$this->dummyQueries = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $query query
|
||||
*
|
||||
* @return object|false
|
||||
*/
|
||||
public function prepare(Connection $connection, string $query)
|
||||
public function prepare(Connection $connection, string $query): ?Statement
|
||||
{
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Loading…
Reference in New Issue
Block a user