index/src/Data/MariaDB/MariaDBStatement.php

166 lines
4.6 KiB
PHP

<?php
// MariaDBStatement.php
// Created: 2021-05-02
// Updated: 2022-02-27
namespace Index\Data\MariaDB;
use mysqli_stmt;
use InvalidArgumentException;
use RuntimeException;
use Index\Data\DbType;
use Index\Data\QueryExecuteException;
use Index\Data\IDbStatement;
use Index\IO\Stream;
/**
* Represents a MariaDB/MySQL prepared statement.
*/
class MariaDBStatement implements IDbStatement {
private mysqli_stmt $statement;
private array $params = [];
/**
* Creates a MariaDBStatement.
*
* @param mysqli_stmt $statement Underlying statement object.
* @return MariaDBStatement A new statement instance.
*/
public function __construct(mysqli_stmt $statement) {
$this->statement = $statement;
}
private static bool $constructed = false;
private static string $resultImplementation;
/**
* Determines which MariaDBResult implementation should be used.
*
* @internal
*/
public static function construct(): void {
if(self::$constructed !== false)
throw new RuntimeException('Static constructor was already called.');
self::$constructed = true;
self::$resultImplementation = function_exists('mysqli_stmt_get_result')
? MariaDBResultNative::class
: MariaDBResultLib::class;
}
public function getParameterCount(): int {
return $this->statement->param_count;
}
public function addParameter(int $ordinal, mixed $value, int $type = DbType::AUTO): void {
if($ordinal < 1 || $ordinal > $this->getParameterCount())
throw new InvalidArgumentException('$ordinal is not a valid parameter number.');
$this->params[$ordinal - 1] = new MariaDBParameter($ordinal, $type, $value);
}
/**
* Gets the last error code.
*
* @return int Last error code.
*/
public function getLastErrorCode(): int {
return $this->statement->errno;
}
/**
* Gets the last error string.
*
* @return string Last error string.
*/
public function getLastErrorString(): string {
return $this->statement->error;
}
/**
* Gets the current SQL State of this statement.
*
* @return string Current SQL State.
*/
public function getSQLState(): string {
return $this->statement->sqlstate;
}
/**
* Gets a list of errors from the last command.
*
* @return array List of last errors.
*/
public function getLastErrors(): array {
return MariaDBWarning::fromLastErrors($this->statement->error_list);
}
/**
* Gets list of warnings.
*
* The result of SHOW WARNINGS;
*
* @return array List of warnings.
*/
public function getWarnings(): array {
return MariaDBWarning::fromGetWarnings($this->statement->get_warnings());
}
public function getResult(): MariaDBResult {
return new self::$resultImplementation($this->statement);
}
public function getLastInsertId(): int|string {
return $this->statement->insert_id;
}
public function execute(): void {
$args = [''];
foreach($this->params as $key => $param) {
$args[0] .= $param->getBindType();
$type = $param->getDbType();
$value = $param->getValue();
if($type === DbType::NULL)
$value = null;
elseif($type === DbType::BLOB) {
if($value instanceof Stream) {
while(($data = $value->read(8192)) !== null)
$this->statement->send_long_data($key, $data);
} elseif(is_resource($value)) {
while($data = fread($value, 8192))
$this->statement->send_long_data($key, $data);
} else
$this->statement->send_long_data($key, strval($value));
$value = null;
}
${"value{$key}"} = $value;
$args[] = &${"value{$key}"};
}
if(!empty($args[0]))
call_user_func_array([$this->statement, 'bind_param'], $args);
if(!$this->statement->execute())
throw new QueryExecuteException($this->getLastErrorString(), $this->getLastErrorCode());
}
public function reset(): void {
$this->params = [];
if(!$this->statement->reset())
throw new QueryExecuteException($this->getLastErrorString(), $this->getLastErrorCode());
}
public function close(): void {
$this->statement->close();
}
public function __destruct() {
$this->close();
}
}
MariaDBStatement::construct();