From fedcd3d0040f0b7df222da0092b15ccf5ee7404f Mon Sep 17 00:00:00 2001 From: "Bruno O. Notario" Date: Wed, 24 Jan 2024 15:30:32 -0300 Subject: [PATCH] Added prepared statements to database class MagiQL also receive load method to load values on SQL statements --- system/MagiQL/MagiQL.php | 13 +++ .../MagiQL/Manipulation/AbstractBaseQuery.php | 16 +++- system/database/autoload.php | 13 +++ system/database/databases/mpdo.php | 64 +++++++++---- system/database/databases/mysql_pdo.php | 48 +++++++++- system/database/databases/mysqli.php | 91 +++++++++++++++++++ system/engine/interfaces/databases.php | 10 ++ 7 files changed, 236 insertions(+), 19 deletions(-) diff --git a/system/MagiQL/MagiQL.php b/system/MagiQL/MagiQL.php index 5666afe..dbd468f 100644 --- a/system/MagiQL/MagiQL.php +++ b/system/MagiQL/MagiQL.php @@ -40,6 +40,19 @@ class MagiQL extends Builder { parent::__construct(); } + /** + * + * @param \Phacil\Framework\MagiQL\Api\QueryInterface $obj + * @return \Phacil\Framework\Databases\Object\ResultInterface|true + * @throws \Phacil\Framework\MagiQL\Builder\BuilderException + * @throws \Phacil\Framework\Exception + */ + public function execute(\Phacil\Framework\MagiQL\Api\QueryInterface $obj) { + $query = $this->write($obj); + $values = $this->getValues(); + return $this->db->execute($query, $values); + } + public function __call($name, $arguments = array()){ return call_user_func_array([$this->queryObj, $name], $arguments); diff --git a/system/MagiQL/Manipulation/AbstractBaseQuery.php b/system/MagiQL/Manipulation/AbstractBaseQuery.php index ab1651c..f1a3234 100644 --- a/system/MagiQL/Manipulation/AbstractBaseQuery.php +++ b/system/MagiQL/Manipulation/AbstractBaseQuery.php @@ -92,7 +92,7 @@ abstract class AbstractBaseQuery implements QueryInterface, QueryPartInterface /** * - * @return \Phacil\Framework\MagiQL\Api\BuilderInterface + * @return \Phacil\Framework\MagiQL\Api\BuilderInterface|\Phacil\Framework\MagiQL * @throws \Phacil\Framework\Exception\RuntimeException */ final public function getBuilder() @@ -280,4 +280,18 @@ abstract class AbstractBaseQuery implements QueryInterface, QueryPartInterface { return $this->comment; } + + /** + * + * @return \Phacil\Framework\Databases\Object\ResultInterface|true|array + * @throws \Phacil\Framework\Exception + */ + public function load() { + try { + return $this->getBuilder()->execute($this); + } catch (\Throwable $th) { + throw new \Phacil\Framework\Exception($th->getMessage()); + } + return []; + } } diff --git a/system/database/autoload.php b/system/database/autoload.php index 114a931..c1eb5f8 100644 --- a/system/database/autoload.php +++ b/system/database/autoload.php @@ -239,4 +239,17 @@ final class Database { $this->$nome = $object; } + + /** + * Execute a prepared statement with parameters + * + * @param string $sql SQL query with named placeholders + * @param array $params Associative array of parameters + * @return \Phacil\Framework\Databases\Object\ResultInterface|true + * @throws \Phacil\Framework\Exception + */ + public function execute($sql, array $params = []) + { + return $this->driver->execute($sql, $params); + } } diff --git a/system/database/databases/mpdo.php b/system/database/databases/mpdo.php index 1d5c715..ccc8b20 100644 --- a/system/database/databases/mpdo.php +++ b/system/database/databases/mpdo.php @@ -30,7 +30,7 @@ final class mPDO implements Databases { /** * - * @var mixed + * @var \PDOStatement */ private $statement = null; @@ -61,22 +61,7 @@ final class mPDO implements Databases { $this->statement->bindParam($parameter, $variable, $data_type); } } - public function execute() { - try { - if ($this->statement && $this->statement->execute()) { - $data = array(); - while ($row = $this->statement->fetch(\PDO::FETCH_ASSOC)) { - $data[] = $row; - } - $result = new \stdClass(); - $result->row = (isset($data[0])) ? $data[0] : array(); - $result->rows = $data; - $result->num_rows = $this->statement->rowCount(); - } - } catch(\PDOException $e) { - throw new \Phacil\Framework\Exception('Error: ' . $e->getMessage() . ' Error Code : ' . $e->getCode()); - } - } + public function query($sql, $params = array()) { $this->statement = $this->connection->prepare($sql); @@ -138,4 +123,49 @@ final class mPDO implements Databases { public function __destruct() { unset($this->connection); } + + /** + * Execute a prepared statement with parameters + * + * @param string $sql SQL query with named placeholders + * @param array $params Associative array of parameters + * @return \Phacil\Framework\Databases\Object\ResultInterface|true + * @throws \Phacil\Framework\Exception + */ + public function execute($sql, array $params = []) + { + try { + $stmt = $this->connection->prepare($sql); + + if ($stmt === false) { + throw new \Phacil\Framework\Exception('Error preparing query: ' . implode(', ', $this->connection->errorInfo())); + } + + // Bind parameters if there are any + if (!empty($params)) { + foreach ($params as $placeholder => &$param) { + $stmt->bindParam($placeholder, $param); + } + } + + $stmt->execute(); + + if ($stmt->columnCount()) { + $data = new \Phacil\Framework\Databases\Object\Result(); + $data->setNumRows($stmt->rowCount()); + $data->setRows($stmt->fetchAll()); + //$data->setRow(isset($data->rows[0]) ? $data->rows[0] : null); + $stmt->closeCursor(); + + return $data; + } else { + $this->rowCount = $stmt->rowCount(); + $stmt->closeCursor(); + + return true; + } + } catch (\PDOException $exception) { + throw new \Phacil\Framework\Exception($exception->getMessage()); + } + } } \ No newline at end of file diff --git a/system/database/databases/mysql_pdo.php b/system/database/databases/mysql_pdo.php index ff413e5..4769020 100644 --- a/system/database/databases/mysql_pdo.php +++ b/system/database/databases/mysql_pdo.php @@ -10,7 +10,7 @@ namespace Phacil\Framework\Databases; use Phacil\Framework\Interfaces\Databases; -final class MYSQL_PDO implements Databases +class MYSQL_PDO implements Databases { /** * Link to the database connection @@ -163,4 +163,50 @@ final class MYSQL_PDO implements Databases { $this->close(); } + + /** + * Execute a prepared statement with parameters + * + * @param string $sql SQL query with named placeholders + * @param array $params Associative array of parameters + * @return \Phacil\Framework\Databases\Object\ResultInterface|true + * @throws \Phacil\Framework\Exception + */ + public function execute($sql, array $params = []) + { + try { + $stmt = $this->dbh->prepare($sql); + + if ($stmt === false) { + throw new \Phacil\Framework\Exception('Error preparing query: ' . implode(', ', $this->dbh->errorInfo())); + } + + // Bind parameters if there are any + if (!empty($params)) { + foreach ($params as $placeholder => &$param) { + $stmt->bindParam($placeholder, $param); + } + } + + $stmt->execute(); + + if ($stmt->columnCount()) { + $data = new \Phacil\Framework\Databases\Object\Result(); + $data->setNumRows($stmt->rowCount()); + $data->setRows($stmt->fetchAll()); + //$data->setRow(isset($data->rows[0]) ? $data->rows[0] : null); + $stmt->closeCursor(); + + return $data; + } else { + $this->affectedRows = $stmt->rowCount(); + $stmt->closeCursor(); + + return true; + + } + } catch (\PDOException $exception) { + throw new \Phacil\Framework\Exception($exception->getMessage()); + } + } } \ No newline at end of file diff --git a/system/database/databases/mysqli.php b/system/database/databases/mysqli.php index 691a8b5..fe55221 100644 --- a/system/database/databases/mysqli.php +++ b/system/database/databases/mysqli.php @@ -115,4 +115,95 @@ class MySQLi implements Databases { public function __destruct() { $this->connection->close(); } + + /** + * Execute a prepared statement with parameters + * + * @param string $sql SQL query with named placeholders + * @param array $params Associative array of parameters + * @return \Phacil\Framework\Databases\Object\ResultInterface|true + * @throws \Phacil\Framework\Exception + */ + public function execute($sql, array $params = []) + { + // Verificar se há parâmetros e fazer o bind + if (!empty($params)) { + $types = ''; + $bindParams = []; + + //$sql = str_replace(array_keys($params), array_fill(0, count($params), '?'), $sql); + + foreach ($params as $placeholder => &$param) { + + //$stmt->bind_param($this->getParamType($param), $param); + $types .= $this->getParamType($param); + $bindParams[] = &$param; + + if (is_string($placeholder)) + $sql = str_replace($placeholder, '?', $sql); + } + + $stmt = $this->connection->prepare($sql); + + array_unshift($bindParams, $types); + call_user_func_array([$stmt, 'bind_param'], $bindParams); + } else { + $stmt = $this->connection->prepare($sql); + } + + if ($stmt === false) { + throw new \Phacil\Framework\Exception('Error preparing query: ' . $this->connection->error); + } + + $result_exec = $stmt->execute(); + + if ($stmt->errno) { + throw new \Phacil\Framework\Exception('Error executing query: ' . $stmt->error); + } + + $result = $stmt->get_result(); + + if ($result === false && !empty($stmt->error_list)) { + throw new \Phacil\Framework\Exception('Error getting result: ' . $stmt->error); + } + + // Processar resultados se for um SELECT + if ($result instanceof \mysqli_result) { + $data = []; + while ($row = $result->fetch_assoc()) { + $data[] = $row; + } + + $resultObj = new \Phacil\Framework\Databases\Object\Result(); + $resultObj->setNumRows($result->num_rows); + $resultObj->setRow(isset($data[0]) ? $data[0] : []); + $resultObj->setRows($data); + + $result->close(); + + return $resultObj; + } + + // Se não for um SELECT, apenas retornar verdadeiro + return $result_exec; + } + + /** + * Check type + * @param mixed $value + * @return string + */ + private function getParamType($value) + { + switch (true) { + case is_int($value): + return 'i'; + case is_float($value): + return 'd'; + case is_string($value): + return 's'; + default: + return 'b'; + } + } } diff --git a/system/engine/interfaces/databases.php b/system/engine/interfaces/databases.php index b52da9a..54b95a9 100644 --- a/system/engine/interfaces/databases.php +++ b/system/engine/interfaces/databases.php @@ -64,4 +64,14 @@ * * @return void */ public function __destruct(); + + /** + * Execute a prepared statement with parameters + * + * @param string $sql SQL query with named placeholders + * @param array $params Associative array of parameters + * @return \Phacil\Framework\Databases\Object\ResultInterface|true + * @throws \Phacil\Framework\Exception + */ + public function execute($sql, array $params = []); } \ No newline at end of file