diff --git a/AbstractDB.php b/AbstractDB.php
index e25cfbe..04b62bf 100755
--- a/AbstractDB.php
+++ b/AbstractDB.php
@@ -1,15 +1,17 @@
connKey = is_null($key) ? "default" : $key;
}
-
- public function connInst() {
+
+ /**
+ * @throws ConnectException
+ */
+ public function connInst(): Connect
+ {
return Connect::getInstance($this->connKey);
}
-
+
/**
* Access Mysql DB connection
- * @return \mysqli
+ * @return ConnectInterface
+ * @throws ConnectException
*/
- public function connect()
+ public function connect(): ConnectInterface
{
return $this->connInst()->DB();
}
/**
* Get current instance Table name with prefix attached
+ * @param bool $withAlias
* @return string
+ * @throws ConnectException
*/
public function getTable(bool $withAlias = false): string
{
- $alias = ($withAlias && !is_null($this->alias)) ? " {$this->alias}" : "";
+ $alias = ($withAlias && !is_null($this->alias)) ? " $this->alias" : "";
return $this->connInst()->getHandler()->getPrefix() . $this->table . $alias;
}
/**
* Get current instance Columns
* @return array
+ * @throws DBValidationException
*/
public function getColumns(): array
{
@@ -137,7 +154,6 @@ protected function getAttr(array|string|int|float $value): AttrInterface
return new Attr($value);
}
-
/**
* Will reset Where input
* @return void
@@ -191,17 +207,18 @@ protected function joinTypes(string $val): string
}
/**
- * Sperate Alias
- * @param string|array $data
+ * Separate Alias
+ * @param string|array $data
* @return array
+ * @throws ResultException
*/
- final protected function sperateAlias(string|array $data): array
+ final protected function separateAlias(string|array $data): array
{
$alias = null;
$table = $data;
if (is_array($data)) {
if (count($data) !== 2) {
- throw new DBQueryException("If you specify Table as array then it should look " .
+ throw new ResultException("If you specify Table as array then it should look " .
"like this [TABLE_NAME, ALIAS]", 1);
}
$alias = array_pop($data);
@@ -211,15 +228,16 @@ final protected function sperateAlias(string|array $data): array
}
/**
- * Propegate where data structure
- * @param string|AttrInterface $key
+ * Propagate where data structure
+ * @param string|AttrInterface $key
* @param string|int|float|AttrInterface $val
* @param array|null &$data static value
+ * @throws DBValidationException
*/
final protected function setWhereData(string|AttrInterface $key, string|int|float|AttrInterface $val, ?array &$data): void
{
if (is_null($data)) {
- $data = array();
+ $data = [];
}
$key = (string)$this->prep($key, false);
$val = $this->prep($val);
@@ -228,12 +246,12 @@ final protected function setWhereData(string|AttrInterface $key, string|int|floa
throw new DBValidationException($this->mig->getMessage(), 1);
}
- //$data[$this->whereIndex][$this->whereAnd][$this->compare][$key][] = $val;
$data[$this->whereIndex][$this->whereAnd][$key][] = [
"not" => $this->whereNot,
"operator" => $this->compare,
"value" => $val
];
+ $this->whereNot = null;
$this->whereProtocol[$key][] = $val;
$this->resetWhere();
@@ -251,27 +269,26 @@ final protected function whereArrToStr(array $array): string
foreach ($array as $key => $arr) {
foreach ($arr as $col => $a) {
if (is_array($a)) {
- foreach ($a as $int => $row) {
+ foreach ($a as $row) {
if ($count > 0) {
- $out .= "{$key} ";
+ $out .= "$key ";
}
if ($row['not'] === true) {
$out .= "NOT ";
}
- $out .= "{$col} {$row['operator']} {$row['value']} ";
+ $out .= "$col {$row['operator']} {$row['value']} ";
$count++;
}
-
+
} else {
- $out .= "{$key} {$a} ";
+ $out .= ($count) > 0 ? "$key $a " : $a;
$count++;
}
}
}
-
return $out;
}
-
+
/**
* Get the Main FK data protocol
* @return array
@@ -279,7 +296,7 @@ final protected function whereArrToStr(array $array): string
final protected function getMainFKData(): array
{
if (is_null($this->fkData)) {
- $this->fkData = array();
+ $this->fkData = [];
foreach ($this->mig->getMig()->getData() as $col => $row) {
if (isset($row['fk'])) {
foreach ($row['fk'] as $a) {
@@ -315,7 +332,7 @@ final protected function prep(mixed $val, bool $enclose = true): AttrInterface
*/
final protected function prepArr(array $arr, bool $enclose = true): array
{
- $new = array();
+ $new = [];
foreach ($arr as $pKey => $pVal) {
$key = (string)$this->prep($pKey, false);
$new[$key] = (string)$this->prep($pVal, $enclose);
@@ -324,12 +341,12 @@ final protected function prepArr(array $arr, bool $enclose = true): array
}
/**
- * Use vsprintf to mysql prep/protect input in string. Prep string values needs to be eclosed manually
+ * Use vsprintf to mysql prep/protect input in string. Prep string values needs to be enclosed manually
* @param string $str SQL string example: (id = %d AND permalink = '%s')
* @param array $arr Mysql prep values
* @return string
*/
- final protected function sprint(string $str, array $arr = array()): string
+ final protected function sprint(string $str, array $arr = []): string
{
return vsprintf($str, $this->prepArr($arr, false));
}
@@ -351,7 +368,8 @@ final protected function camelLoop(array $camelCaseArr, array $valArr, callable
}
/**
- * Will extract camle case to array
+ * MOVE TO DTO ARR
+ * Will extract camelcase to array
* @param string $value string value with possible camel cases
* @return array
*/
@@ -369,7 +387,7 @@ final protected function extractCamelCase(string $value): array
*/
final protected function buildJoinFromMig(MigrateInterface $mig, string $type): array
{
- $joinArr = array();
+ $joinArr = [];
$prefix = $this->connInst()->getHandler()->getPrefix();
$main = $this->getMainFKData();
$data = $mig->getData();
@@ -403,6 +421,7 @@ final protected function buildJoinFromMig(MigrateInterface $mig, string $type):
/**
* Build on YB to col sql string part
* @return string|null
+ * @throws ConnectException
*/
protected function getAllQueryTables(): ?string
{
@@ -420,7 +439,7 @@ protected function getAllQueryTables(): ?string
* @param string|null $method
* @param array $args
* @return array|object|bool|string
- * @throws DBQueryException
+ * @throws ResultException|ConnectException
*/
final protected function query(string|self $sql, ?string $method = null, array $args = []): array|object|bool|string
{
@@ -430,8 +449,8 @@ final protected function query(string|self $sql, ?string $method = null, array $
if (method_exists($query, $method)) {
return call_user_func_array([$query, $method], $args);
}
- throw new DBQueryException("Method \"$method\" does not exists!", 1);
+ throw new ResultException("Method \"$method\" does not exists!", 1);
}
return $query;
}
-}
\ No newline at end of file
+}
diff --git a/AbstractMigrate.php b/AbstractMigrate.php
index d98ed0f..7b414c2 100755
--- a/AbstractMigrate.php
+++ b/AbstractMigrate.php
@@ -1,4 +1,5 @@
handler = $handler;
}
/**
- * Prevent cloning the instance
+ * This will prevent cloning the instance
* @return void
*/
- private function __clone() {
+ private function __clone(): void
+ {
+ }
+
+ /**
+ * Access the database main class
+ * @param string $method
+ * @param array $arguments
+ * @return object|false
+ * @throws ConnectException
+ */
+ public function __call(string $method, array $arguments): object|false
+ {
+ if(is_null($this->connection)) {
+ throw new ConnectException("The connection has not been initialized yet.");
+ }
+ return call_user_func_array([$this->connection, $method], $arguments);
+ }
+
+ /**
+ * Get default instance or secondary instances with key
+ * @param string|null $key
+ * @return self
+ * @throws ConnectException
+ */
+ public static function getInstance(?string $key = null): self
+ {
+ $key = self::getKey($key);
+ if(!self::hasInstance($key)) {
+ throw new ConnectException("Connection Error: No active connection or connection instance found.");
+ }
+ self::$current = $key;
+ return self::$inst[$key];
}
/**
@@ -42,17 +83,17 @@ private function __clone() {
public static function __callStatic(string $name, array $arguments)
{
$inst = new DB();
- $inst->setConnKey(self::$current);
+ $inst->setConnKey(static::$current);
return $inst::$name(...$arguments);
}
/**
* Set connection handler
- * @param $handler
+ * @param HandlerInterface $handler
* @param string|null $key
* @return self
*/
- public static function setHandler($handler, ?string $key = null): self
+ public static function setHandler(HandlerInterface $handler, ?string $key = null): self
{
$key = self::getKey($key);
if(self::hasInstance($key)) {
@@ -81,22 +122,6 @@ public static function removeHandler(string $key): void
}
}
- /**
- * Get default instance or secondary instances with key
- * @param string|null $key
- * @return self
- * @throws ConnectException
- */
- public static function getInstance(?string $key = null): self
- {
- $key = self::getKey($key);
- if(!self::hasInstance($key)) {
- throw new ConnectException("Connection Error: No active connection or connection instance found.");
- }
- self::$current = $key;
- return self::$inst[$key];
- }
-
/**
* Check if default instance or secondary instances exist for key
* @param string|null $key
@@ -109,39 +134,44 @@ public static function hasInstance(?string $key = null): bool
}
/**
- * Get the possible connection key
- * @param string|null $key
- * @return string
+ * Connect to database
+ * The ConnectInterface instance will be null before execute
+ * @return void
+ * @throws ConnectException
*/
- private static function getKey(?string $key = null): string
+ public function execute(): void
{
- $key = (is_null($key)) ? "default" : $key;
- return $key;
+ try {
+ $this->connection = $this->handler->execute();
+ } catch(Exception $e) {
+ throw new ConnectException($e->getMessage(), $e->getCode(), $e);
+ }
}
/**
- * Access the connection handler
- * @return mixed
+ * Get current DB connection
+ * DEPRECATED: Use connection instead!
*/
- function getHandler() {
- return $this->handler;
+ public function DB(): ConnectInterface
+ {
+ return $this->connection();
}
/**
- * Get database type
- * @return string
+ * Get current DB connection
*/
- public function getType(): string
+ public function connection(): ConnectInterface
{
- return $this->handler->getType();
+ return $this->connection;
}
+
/**
- * Get current table prefix
- * @return string
+ * Access the connection handler
+ * @return HandlerInterface
*/
- public function getPrefix(): string
+ public function getHandler(): HandlerInterface
{
- return $this->handler->getPrefix();
+ return $this->handler;
}
/**
@@ -154,103 +184,141 @@ public function hasConnection(): bool
}
/**
- * Connect to database
- * @return void
+ * Protect/prep database values from injections
+ * @param string $value
+ * @return string
*/
- public function execute(): void
+ public function prep(string $value): string
{
- $this->db = $this->handler->execute();
+ return $this->handler->prep($value);
}
/**
- * Get current DB connection
+ * Query sql string
+ * @param string $query
+ * @param int $result_mode
+ * @return object|array|bool
+ * @throws ResultException
*/
- public function DB(): mixed
+ public function query(string $query, int $result_mode = 0): object|array|bool
{
- return $this->db;
+ try {
+ return $this->connection->query($query);
+ } catch (Exception $e) {
+ throw new ResultException($e->getMessage(), $e->getCode(), $e);
+ }
}
/**
- * Query sql string
- * @param string $sql
- * @return object|array|bool
+ * Begin transaction
+ * @return bool
*/
- public function query(string $sql): object|array|bool
+ public function begin_transaction(): bool
{
- return $this->db->query($sql);
+ return $this->connection->begin_transaction();
}
/**
- * Protect/prep database values from injections
- * @param string $value
- * @return string
+ * Commit transaction
+ * @return bool
*/
- public function prep(string $value): string
+ public function commit(): bool
{
- return $this->handler->prep($value);
+ return $this->connection->commit();
}
/**
- * Select a new database
- * @param string $databaseName
- * @param string|null $prefix Expected table prefix (NOT database prefix)
- * @return void
+ * Rollback transaction
+ * @return bool
*/
- /*
- public static function selectDB(string $databaseName, ?string $prefix = null): void
+ public function rollback(): bool
{
- mysqli_select_db(static::$selectedDB, $databaseName);
- if (!is_null($prefix)) {
- static::setPrefix($prefix);
- }
+ return $this->connection->rollback();
}
+
+ /**
+ * Returns the value generated for an AI column by the last query
+ * @param string|null $column Is only used with PostgreSQL!
+ * @return int
+ */
+ public function insert_id(?string $column = null): int
+ {
+ return $this->connection->insert_id($column);
+ }
+
+ /**
+ * Close connection
+ * @return bool
*/
+ public function close(): true
+ {
+ return $this->connection->close();
+ }
/**
- * Execute multiple quries at once (e.g. from a sql file)
- * @param string $sql
- * @param object|null &$mysqli
- * @return array
+ * Start Transaction will return instance of ConnectInterface instead of bool
+ * @return ConnectInterface
+ * @throws ConnectException
*/
- public function multiQuery(string $sql, object &$mysqli = null): array
+ public function transaction(): ConnectInterface
{
- return $this->handler->multiQuery($sql, $mysqli);
+ if(!$this->begin_transaction()) {
+ $errorMsg = "Couldn't start transaction!";
+ if(!empty($this->connection->error)) {
+ $errorMsg = "The transaction error: " . $this->connection->error;
+ }
+ throw new ConnectException($errorMsg);
+ }
+ return $this->connection;
}
/**
- * Start Transaction
- * @return mysqli
+ * Get the possible connection key
+ * @param string|null $key
+ * @return string
*/
- public function transaction(): mixed
+ private static function getKey(?string $key = null): string
{
- return $this->handler->transaction();
+ return (is_null($key)) ? "default" : $key;
}
/**
- * Profile mysql speed
+ * MOVE TO HANDLERS
+ * This method will be CHANGED soon
+ * @param string|null $key
+ * @return self
+ * @throws ConnectException|ResultException
*/
- public static function startProfile(): void
+ public static function startProfile(?string $key = null): self
{
- Connect::query("set profiling=1");
+ $inst = self::getInstance($key);
+ $inst->query("set profiling=1");
+ return $inst;
}
/**
+ * MOVE TO HANDLERS
+ * This method will be CHANGED soon
* Close profile and print results
+ * Expects startProfile
+ * @throws ResultException
*/
- public static function endProfile($html = true): string|array
+ public function endProfile($html = true): string|array
{
$totalDur = 0;
- $result = Connect::query("show profiles");
+ $result = $this->query("show profiles");
$output = "";
if ($html) {
$output .= "
";
}
- if (is_object($result)) while ($row = $result->fetch_object()) {
- $dur = round($row->Duration, 4) * 1000;
- $totalDur += $dur;
- $output .= $row->Query_ID . ' - ' . $dur . ' ms - ' . $row->Query . "
\n";
+ if (is_object($result)) {
+ while ($row = $result->fetch_object()) {
+ $dur = round($row->Duration, 4) * 1000;
+ $totalDur += $dur;
+ $output .= $row->Query_ID . ' - ' . $dur . ' ms - ' . $row->Query . "
\n";
+ }
}
$total = round($totalDur, 4);
@@ -259,55 +327,7 @@ public static function endProfile($html = true): string|array
$output .= "
";
return $output;
} else {
- return array("row" => $output, "total" => $total);
- }
- }
-
- /**
- * Create Mysql variable
- * @param string $key Variable key
- * @param string $value Variable value
- */
- public static function setVariable(string $key, string $value): AttrInterface
- {
- $escapedVarName = Attr::value("@{$key}")->enclose(false)->encode(false);
- $escapedValue = (($value instanceof AttrInterface) ? $value : Attr::value($value));
-
- self::$mysqlVars[$key] = clone $escapedValue;
- Connect::query("SET {$escapedVarName} = {$escapedValue}");
- return $escapedVarName;
- }
-
- /**
- * Get Mysql variable
- * @param string $key Variable key
- */
- public static function getVariable(string $key): AttrInterface
- {
- if (!self::hasVariable($key)) {
- throw new ConnectException("DB MySQL variable is not set.", 1);
- }
- return Attr::value("@{$key}")->enclose(false)->encode(false);
- }
-
- /**
- * Get Mysql variable
- * @param string $key Variable key
- */
- public static function getVariableValue(string $key): string
- {
- if (!self::hasVariable($key)) {
- throw new ConnectException("DB MySQL variable is not set.", 1);
+ return ["row" => $output, "total" => $total];
}
- return self::$mysqlVars[$key]->enclose(false)->encode(false);
- }
-
- /**
- * Has Mysql variable
- * @param string $key Variable key
- */
- public static function hasVariable(string $key): bool
- {
- return (isset(self::$mysqlVars[$key]));
}
}
diff --git a/ConnectTest.php b/ConnectTest.php
new file mode 100755
index 0000000..38f2d7f
--- /dev/null
+++ b/ConnectTest.php
@@ -0,0 +1,41 @@
+handler = $handler;
+
+ if(is_null(self::$inst)) {
+ self::$inst = $this;
+ }
+ }
+
+ public static function getConnection(): self
+ {
+ return self::$inst;
+ }
+
+ public static function __callStatic(string $name, array $arguments): mixed
+ {
+ $inst = DBTest::{$name}(...$arguments);
+ $inst->setConnection(self::$inst);
+ }
+
+
+}
diff --git a/Create.php b/Create.php
index b805496..e013cf4 100755
--- a/Create.php
+++ b/Create.php
@@ -67,13 +67,14 @@
namespace MaplePHP\Query;
+use MaplePHP\Query\Exceptions\ConnectException;
use MaplePHP\Query\Exceptions\QueryCreateException;
class Create
{
private $sql;
private $add;
- private $addArr = array();
+ private $addArr = [];
private $prefix;
private $type;
private $args;
@@ -87,15 +88,15 @@ class Create
private $tbKeys;
private $tbKeysType;
//private $columnData;
- private $keys = array();
- private $ai = array();
- private $fk = array();
- private $fkList = array();
- private $colData = array();
- private $rename = array();
- private $hasRename = array();
- private $renameTable = array();
- private $primaryKeys = array();
+ private $keys = [];
+ private $ai = [];
+ private $fk = [];
+ private $fkList = [];
+ private $colData = [];
+ private $rename = [];
+ private $hasRename = [];
+ private $renameTable = [];
+ private $primaryKeys = [];
private $dropPrimaryKeys = false;
private $build;
@@ -248,7 +249,7 @@ public function generated()
{
if (isset($this->args['generated'])) {
$value = explode(",", $this->args['generated']['columns']);
- $colArr = array();
+ $colArr = [];
if (isset($this->args['generated']['json_columns'])) {
foreach ($value as $col) {
preg_match('#\{{(.*?)\}}#', $col, $match);
@@ -292,8 +293,8 @@ public function generated()
*/
private function adding()
{
- $arr = array();
- $methodArr = array("type", "generated", "attributes", "collation", "null", "default");
+ $arr = [];
+ $methodArr = ["type", "generated", "attributes", "collation", "null", "default"];
foreach ($methodArr as $method) {
if ($val = $this->{$method}()) {
$arr[] = $val;
@@ -536,17 +537,18 @@ public function build()
/**
* Execute
* @return array errors.
+ * @throws ConnectException
*/
public function execute()
{
$sql = $this->build();
- $error = Connect::getInstance()->multiQuery($sql, $mysqli);
+ $error = Connect::getInstance()->getHandler()->multiQuery($sql, $mysqli);
return $error;
}
public function mysqlCleanArr(array $arr)
{
- $new = array();
+ $new = [];
foreach ($arr as $a) {
$new[] = Connect::getInstance()->prep($a);
}
@@ -560,7 +562,7 @@ public function mysqlCleanArr(array $arr)
private function tbKeys(): array
{
if (is_null($this->tbKeys)) {
- $this->tbKeysType = $this->tbKeys = array();
+ $this->tbKeysType = $this->tbKeys = [];
if ($this->tableExists($this->table)) {
$result = Connect::getInstance()->query("SHOW INDEXES FROM {$this->table}");
if (is_object($result) && $result->num_rows > 0) {
@@ -816,7 +818,7 @@ private function buildKeys(): string
$prepareDrop = $this->tbKeysType();
if (count($this->keys) > 0) {
- $sqlKeyArr = array();
+ $sqlKeyArr = [];
foreach ($this->keys as $col => $key) {
$col = Connect::getInstance()->prep($col);
$key = strtoupper(Connect::getInstance()->prep($key));
@@ -917,7 +919,7 @@ public function fkExists(string $table, string $col)
"INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE REFERENCED_TABLE_SCHEMA = '{$dbName}' AND " .
"TABLE_NAME = '{$table}' AND COLUMN_NAME = '{$col}'");
- $arr = array();
+ $arr = [];
if (is_object($result) && $result->num_rows > 0) {
while ($row = $result->fetch_object()) {
$arr[$row->CONSTRAINT_NAME] = $row;
@@ -954,4 +956,4 @@ public function columnExists(string $table, string $col)
}
return false;
}
-}
\ No newline at end of file
+}
diff --git a/DB.php b/DB.php
index 97714ef..fdf0d89 100755
--- a/DB.php
+++ b/DB.php
@@ -1,14 +1,16 @@
method = $method;
- $inst->setConnKey(Connect::$current);
- $prefix = Connect::getInstance(Connect::$current)->getHandler()->getPrefix();
+ //$inst->setConnKey(Connect::$current);
+ $prefix = $inst->connInst()->getHandler()->getPrefix();
switch ($inst->method) {
case 'select':
@@ -83,6 +84,7 @@ public static function __callStatic(string $method, array $args)
} else {
$inst = new self();
+ //$inst->setConnKey(Connect::$current);
}
return $inst;
@@ -93,8 +95,8 @@ public static function __callStatic(string $method, array $args)
* @param string $method
* @param array $args
* @return array|bool|DB|object
- * @throws DBQueryException
- * @throws DBValidationException|ConnectException
+ * @throws ResultException
+ * @throws DBValidationException|ConnectException|ResultException|Exceptions\DBQueryException
*/
public function __call(string $method, array $args)
{
@@ -104,7 +106,7 @@ public function __call(string $method, array $args)
case "pluck": // Columns??
$args = ($args[0] ?? "");
if (str_contains($args, ",")) {
- throw new DBQueryException("Your only allowed to pluck one database column!");
+ throw new ResultException("Your only allowed to pluck one database column!");
}
$pluck = explode(".", $args);
@@ -141,7 +143,7 @@ public function __call(string $method, array $args)
* It is better to use (DB::select, DB::insert, DB::update, DB::delete)
* @param string|array|MigrateInterface $data
* @return self new instance
- * @throws DBQueryException
+ * @throws ResultException
*/
public static function table(string|array|MigrateInterface $data): self
{
@@ -152,7 +154,7 @@ public static function table(string|array|MigrateInterface $data): self
}
$inst = new self();
- $data = $inst->sperateAlias($data);
+ $data = $inst->separateAlias($data);
$inst->alias = $data['alias'];
$inst->table = $inst->getAttr($data['table'])->enclose(false);
$inst->mig = $mig;
@@ -190,7 +192,7 @@ public static function withAttr(array|string|int|float $value, ?array $args = nu
* Build SELECT sql code (The method will be auto called in method build)
* @method static __callStatic
* @return self
- * @throws DBValidationException
+ * @throws DBValidationException|ConnectException
*/
protected function select(): self
{
@@ -209,6 +211,7 @@ protected function select(): self
* Select view
* @return self
* @throws DBValidationException
+ * @throws ConnectException
*/
protected function selectView(): self
{
@@ -218,6 +221,7 @@ protected function selectView(): self
/**
* Build INSERT sql code (The method will be auto called in method build)
* @return self
+ * @throws ConnectException
*/
protected function insert(): self
{
@@ -229,6 +233,7 @@ protected function insert(): self
/**
* Build UPDATE sql code (The method will be auto called in method build)
* @return self
+ * @throws ConnectException
*/
protected function update(): self
{
@@ -244,6 +249,7 @@ protected function update(): self
/**
* Build DELETE sql code (The method will be auto called in method build)
* @return self
+ * @throws ConnectException
*/
protected function delete(): self
{
@@ -259,48 +265,6 @@ protected function delete(): self
return $this;
}
- /**
- * Build CREATE VIEW sql code (The method will be auto called in method build)
- * @return self
- */
- protected function createView(): self
- {
- //$this->select();
- $this->sql = "CREATE VIEW " . $this->viewName . " AS $this->sql";
- return $this;
- }
-
- /**
- * Build CREATE OR REPLACE VIEW sql code (The method will be auto called in method build)
- * @return self
- */
- protected function replaceView(): self
- {
- //$this->select();
- $this->sql = "CREATE OR REPLACE VIEW " . $this->viewName . " AS $this->sql";
- return $this;
- }
-
- /**
- * Build DROP VIEW sql code (The method will be auto called in method build)
- * @return self
- */
- protected function dropView(): self
- {
- $this->sql = "DROP VIEW " . $this->viewName;
- return $this;
- }
-
- /**
- * Build DROP VIEW sql code (The method will be auto called in method build)
- * @return self
- */
- protected function showView(): self
- {
- $this->sql = "SHOW CREATE VIEW " . $this->viewName;
- return $this;
- }
-
/**
* Select protected mysql columns
* @param string $columns
@@ -385,19 +349,19 @@ public function whereRaw(string $sql, ...$arr): self
/**
* Create protected MySQL WHERE input
* Supports dynamic method name calls like: whereIdStatus(1, 0)
- * @param string|AttrInterface $key Mysql column
- * @param string|int|float|AttrInterface $val Equals to value
+ * @param string|AttrInterface $column Mysql column
+ * @param string|int|float|AttrInterface $value Equals to value
* @param string|null $operator Change comparison operator from default "=".
* @return self
* @throws DBValidationException
*/
- public function where(string|AttrInterface $key, string|int|float|AttrInterface $val, ?string $operator = null): self
+ public function where(string|AttrInterface $column, string|int|float|AttrInterface $value, ?string $operator = null): self
{
// Whitelist operator
if (!is_null($operator)) {
$this->compare = $this->operator($operator);
}
- $this->setWhereData($key, $val, $this->where);
+ $this->setWhereData($column, $value, $this->where);
return $this;
}
@@ -419,18 +383,18 @@ public function whereBind(callable $call): self
/**
* Create protected MySQL HAVING input
- * @param string|AttrInterface $key Mysql column
- * @param string|int|float|AttrInterface $val Equals to value
+ * @param string|AttrInterface $column Mysql column
+ * @param string|int|float|AttrInterface $value Equals to value
* @param string|null $operator Change comparison operator from default "=".
* @return self
* @throws DBValidationException
*/
- public function having(string|AttrInterface $key, string|int|float|AttrInterface $val, ?string $operator = null): self
+ public function having(string|AttrInterface $column, string|int|float|AttrInterface $value, ?string $operator = null): self
{
if (!is_null($operator)) {
$this->compare = $this->operator($operator);
}
- $this->setWhereData($key, $val, $this->having);
+ $this->setWhereData($column, $value, $this->having);
return $this;
}
@@ -479,20 +443,20 @@ public function offset(int $offset): self
/**
* Set Mysql ORDER
- * @param string|AttrInterface $col Mysql Column
+ * @param string|AttrInterface $column Mysql Column
* @param string $sort Mysql sort type. Only "ASC" OR "DESC" is allowed, anything else will become "ASC".
* @return self
* @throws DBValidationException
*/
- public function order(string|AttrInterface $col, string $sort = "ASC"): self
+ public function order(string|AttrInterface $column, string $sort = "ASC"): self
{
- $col = $this->prep($col, false);
+ $column = $this->prep($column, false);
- if (!is_null($this->mig) && !$this->mig->columns([(string)$col])) {
+ if (!is_null($this->mig) && !$this->mig->columns([(string)$column])) {
throw new DBValidationException($this->mig->getMessage(), 1);
}
$sort = $this->orderSort($sort); // Whitelist
- $this->order[] = "$col $sort";
+ $this->order[] = "$column $sort";
return $this;
}
@@ -546,29 +510,29 @@ public function returning(string $column): self
* @param string $type Type of join
* @return self
* @throws ConnectException
- * @throws DBQueryException
+ * @throws ResultException
* @throws DBValidationException
*/
public function join(
string|array|MigrateInterface $table,
string|array $where = null,
- array $sprint = array(),
+ array $sprint = [],
string $type = "INNER"
): self {
if ($table instanceof MigrateInterface) {
$this->join = array_merge($this->join, $this->buildJoinFromMig($table, $type));
} else {
if (is_null($where)) {
- throw new DBQueryException("You need to specify the argument 2 (where) value!", 1);
+ throw new ResultException("You need to specify the argument 2 (where) value!", 1);
}
$prefix = $this->connInst()->getHandler()->getPrefix();
- $arr = $this->sperateAlias($table);
+ $arr = $this->separateAlias($table);
$table = (string)$this->prep($arr['table'], false);
$alias = (!is_null($arr['alias'])) ? " {$arr['alias']}" : " $table";
if (is_array($where)) {
- $data = array();
+ $data = [];
foreach ($where as $key => $val) {
if (is_array($val)) {
foreach ($val as $grpKey => $grpVal) {
@@ -606,7 +570,8 @@ public function distinct(): self
}
/**
- * Explain the mysql query. Will tell you how you can make improvements
+ * Explain the query. Will tell you how you can make improvements
+ * All database handlers is supported e.g. mysql, postgresql, sqlite...
* @return self
*/
public function explain(): self
@@ -617,6 +582,7 @@ public function explain(): self
/**
* Disable mysql query cache
+ * All database handlers is supported e.g. mysql, postgresql, sqlite...
* @return self
*/
public function noCache(): self
@@ -627,6 +593,7 @@ public function noCache(): self
/**
* DEPRECATE: Calculate rows in query
+ * All database handlers is supported e.g. mysql, postgresql, sqlite...
* @return self
*/
public function calcRows(): self
@@ -676,7 +643,7 @@ public function onDupKey($key = null, ?string $value = null): self
// Same as onDupKey
public function onDuplicateKey($key = null, ?string $value = null): self
{
- $this->dupSet = array();
+ $this->dupSet = [];
if (!is_null($key)) {
if (is_array($key)) {
$this->dupSet = $this->prepArr($key);
@@ -700,14 +667,14 @@ public function union(DBInterface $inst, bool $allowDuplicate = false): self
return $this->unionRaw($inst->sql(), $allowDuplicate);
}
- /**
- * Union raw result, create union with raw SQL code
- * @param string $sql
- * @param bool $allowDuplicate UNION by default selects only distinct values.
- * Use UNION ALL to also select duplicate values!
- * @mixin AbstractDB
- * @return self
- */
+ /**
+ * Union raw result, create union with raw SQL code
+ * @param string $sql
+ * @param bool $allowDuplicate UNION by default selects only distinct values.
+ * Use UNION ALL to also select duplicate values!
+ * @mixin AbstractDB
+ * @return self
+ */
public function unionRaw(string $sql, bool $allowDuplicate = false): self
{
$this->order = null;
@@ -742,7 +709,7 @@ private function buildUpdateSet(?array $arr = null): string
if (is_null($arr)) {
$arr = $this->set;
}
- $new = array();
+ $new = [];
foreach ($arr as $key => $val) {
$new[] = "$key = $val";
}
@@ -753,6 +720,7 @@ private function buildUpdateSet(?array $arr = null): string
* Will build a returning value that can be fetched with insert id
* This is a PostgreSQL specific function.
* @return string
+ * @throws ConnectException
*/
private function buildReturning(): string
{
@@ -792,6 +760,7 @@ private function buildWhere(string $prefix, ?array $where): string
$out .= (($index > 0) ? " $firstAnd" : "") . " (";
$out .= $this->whereArrToStr($array);
$out .= ")";
+
$index++;
}
}
@@ -822,15 +791,18 @@ private function buildLimit(): string
/**
* Used to call method that builds SQL queries
- * @throws DBQueryException|DBValidationException
+ * @throws ResultException|DBValidationException|ConnectException
*/
final protected function build(): void
{
+
if (!is_null($this->method) && method_exists($this, $this->method)) {
+
+
$inst = (!is_null($this->dynamic)) ? call_user_func_array($this->dynamic[0], $this->dynamic[1]) : $this->{$this->method}();
if (is_null($inst->sql)) {
- throw new DBQueryException("The Method 1 \"$inst->method\" expect to return a sql " .
+ throw new ResultException("The Method 1 \"$inst->method\" expect to return a sql " .
"building method (like return @select() or @insert()).", 1);
}
} else {
@@ -841,7 +813,7 @@ final protected function build(): void
/**
* Generate SQL string of current instance/query
* @return string
- * @throws DBQueryException|DBValidationException
+ * @throws ConnectException|DBValidationException|DBQueryException|ResultException
*/
public function sql(): string
{
@@ -851,20 +823,62 @@ public function sql(): string
/**
* Get insert AI ID from prev inserted result
+ * @param string|null $column
* @return int|string
- * @throws ConnectException|DBQueryException
+ * @throws ConnectException
*/
- public function insertID(): int|string
+ public function insertId(?string $column = null): int|string
{
- if($this->connInst()->getHandler()->getType() === "postgresql") {
- if(is_null($this->returning)) {
- throw new DBQueryException("You need to specify the returning column when using PostgreSQL.");
- }
- return $this->connInst()->DB()->insert_id($this->returning);
- }
- if($this->connInst()->getHandler()->getType() === "sqlite") {
- return $this->connInst()->DB()->lastInsertRowID();
+ $column = !is_null($column) ? $column : $this->returning;
+ if(!is_null($column)) {
+ return $this->connInst()->DB()->insert_id($column);
}
- return $this->connInst()->DB()->insert_id;
+ return $this->connInst()->DB()->insert_id();
+ }
+
+ /**
+ * DEPRECATED??
+ */
+
+ /**
+ * Build CREATE VIEW sql code (The method will be auto called in method build)
+ * @return self
+ */
+ protected function createView(): self
+ {
+ //$this->select();
+ $this->sql = "CREATE VIEW " . $this->viewName . " AS $this->sql";
+ return $this;
+ }
+
+ /**
+ * Build CREATE OR REPLACE VIEW sql code (The method will be auto called in method build)
+ * @return self
+ */
+ protected function replaceView(): self
+ {
+ //$this->select();
+ $this->sql = "CREATE OR REPLACE VIEW " . $this->viewName . " AS $this->sql";
+ return $this;
+ }
+
+ /**
+ * Build DROP VIEW sql code (The method will be auto called in method build)
+ * @return self
+ */
+ protected function dropView(): self
+ {
+ $this->sql = "DROP VIEW " . $this->viewName;
+ return $this;
+ }
+
+ /**
+ * Build DROP VIEW sql code (The method will be auto called in method build)
+ * @return self
+ */
+ protected function showView(): self
+ {
+ $this->sql = "SHOW CREATE VIEW " . $this->viewName;
+ return $this;
}
-}
\ No newline at end of file
+}
diff --git a/DBTest.php b/DBTest.php
new file mode 100755
index 0000000..131103c
--- /dev/null
+++ b/DBTest.php
@@ -0,0 +1,675 @@
+handler = $handler;
+ $this->connection = $handler->execute();
+ $this->prefix = $handler->getPrefix();
+ $this->attr = new Attr($this->connection);
+ }
+
+ public function getConnection()
+ {
+ return $this->connection;
+ }
+
+ public function __toString(): string
+ {
+ return $this->sql();
+ }
+
+ /**
+ * Used to make methods into dynamic shortcuts
+ * @param string $method
+ * @param array $args
+ * @return array|bool|object|string
+ * @throws ConnectException
+ * @throws DBValidationException
+ * @throws ResultException
+ */
+ public function __call(string $method, array $args): array|bool|object|string
+ {
+ $camelCaseArr = Helpers::extractCamelCase($method);
+ $shift = array_shift($camelCaseArr);
+
+ $inst = clone $this;
+ switch ($shift) {
+ case "pluck": // Columns??
+ $args = ($args[0] ?? "");
+ if (str_contains($args, ",")) {
+ throw new ResultException("Your only allowed to pluck one database column!");
+ }
+
+ $pluck = explode(".", $args);
+ $inst->pluck = trim(end($pluck));
+ $inst = $inst->columns($args);
+ break;
+ case "where":
+ case "having":
+ Helpers::camelLoop($camelCaseArr, $args, function ($col, $val) use ($shift, &$inst) {
+ $inst = $inst->{$shift}($col, $val);
+ });
+ break;
+ case "order":
+ if ($camelCaseArr[0] === "By") {
+ array_shift($camelCaseArr);
+ }
+ $ace = end($camelCaseArr);
+ foreach ($args as $val) {
+ $inst = $inst->order($val, $ace);
+ }
+ break;
+ case "join":
+ $inst = $inst->join($args[0], ($args[1] ?? null), ($args[2] ?? []), $camelCaseArr[0]);
+ break;
+ default:
+ return $inst->query($inst, $method, $args);
+ }
+ return $inst;
+ }
+
+ // Magic method to dynamically access protected properties
+ public function __get($property)
+ {
+ if (property_exists($this, $property)) {
+ return $this->{$property};
+ }
+ throw new InvalidArgumentException("Property '$property' does not exist");
+ }
+
+ /**
+ * @param string|array|MigrateInterface $table
+ * @return DBTest
+ */
+ public function table(string|array|MigrateInterface $table): self
+ {
+ $inst = clone $this;
+
+ /*
+ if ($table instanceof MigrateInterface) {
+ $inst->migration = new WhitelistMigration($table);
+ $table = $inst->migration->getTable();
+ }
+ */
+ $tableRow = Helpers::separateAlias($table);
+ $table = $inst->prefix . $tableRow['table'];
+
+
+ $inst->table = $inst->attr($table, Attr::COLUMN_TYPE);
+ $inst->alias = $this->attr($tableRow['alias'] ?? $tableRow['table'], Attr::COLUMN_TYPE);
+
+
+ return $inst;
+ }
+
+ /**
+ * Easy way to create a attr/data type for the query string
+ *
+ * @param mixed $value
+ * @param int $type
+ * @return array|AttrInterface
+ */
+ public function attr(mixed $value, int $type): array|AttrInterface
+ {
+ if(is_callable($value)) {
+ $value = $value($this->attr->withValue($value)->type($type));
+ }
+ if(is_array($value)) {
+ return array_map(function ($val) use ($type) {
+ if($val instanceof AttrInterface) {
+ return $val;
+ }
+ return $this->attr->withValue($val)->type($type);
+ }, $value);
+ }
+ if($value instanceof AttrInterface) {
+ return $value;
+ }
+ return $this->attr->withValue($value)->type($type);
+ }
+
+ /**
+ * When SQL query has been triggered then the QueryBuilder should exist
+ * @return QueryBuilderInterface
+ */
+ public function getQueryBuilder(): QueryBuilderInterface
+ {
+ if(is_null($this->builder)) {
+ $this->sql();
+ //throw new BadMethodCallException("The query builder can only be called after query has been built.");
+ }
+ return $this->builder;
+ }
+
+ /**
+ * Select protected mysql columns
+ *
+ * @param string|array|AttrInterface ...$columns
+ * @return self
+ */
+ public function columns(string|array|AttrInterface ...$columns): self
+ {
+ $inst = clone $this;
+ foreach ($columns as $key => $column) {
+ $inst->columns[$key]['alias'] = null;
+ if(is_array($column)) {
+ $alias = reset($column);
+ $column = key($column);
+ $inst->columns[$key]['alias'] = $this->attr($alias, Attr::COLUMN_TYPE);
+ }
+ $inst->columns[$key]['column'] = $this->attr($column, Attr::COLUMN_TYPE);
+ }
+ return $inst;
+ }
+
+ // JUST A IF STATEMENT
+ // whenNot???
+ public function when(bool $bool, callable $func): self
+ {
+ $inst = clone $this;
+ return $inst;
+ }
+
+ // FIXA - SQL STATEMENT EXISTS
+ // existNot???
+ public function exist(callable $func): self
+ {
+ $inst = clone $this;
+ return $inst;
+ }
+
+ /**
+ * Change where compare operator from default "=".
+ * Will change back to default after where method is triggered
+ * @param string $operator once of (">", ">=", "<", "<>", "!=", "<=", "<=>")
+ * @return self
+ */
+ public function compare(string $operator): self
+ {
+ $inst = clone $this;
+ $inst->compare = Helpers::operator($operator);
+ return $inst;
+ }
+
+ /**
+ * Chaining where with mysql "AND" or with "OR"
+ * @return self
+ */
+ public function and(): self
+ {
+ $inst = clone $this;
+ $inst->whereAnd = "AND";
+ return $inst;
+ }
+
+ /**
+ * Chaining where with mysql "AND" or with "OR"
+ * @return self
+ */
+ public function or(): self
+ {
+ $inst = clone $this;
+ $inst->whereAnd = "OR";
+ return $inst;
+ }
+
+ /**
+ * Chaining with where "NOT" ???
+ * @return self
+ */
+ public function not(): self
+ {
+ $inst = clone $this;
+ $inst->whereNot = true;
+ return $inst;
+ }
+
+
+ /**
+ * Create protected MySQL WHERE input
+ * Supports dynamic method name calls like: whereIdStatus(1, 0)
+ * @param string|AttrInterface $column Mysql column
+ * @param string|int|float|AttrInterface $value Equals to value
+ * @param string|null $operator Change comparison operator from default "=".
+ * @return self
+ */
+ public function where(string|AttrInterface $column, string|int|float|AttrInterface $value, ?string $operator = null): self
+ {
+ $inst = clone $this;
+ if (!is_null($operator)) {
+ $inst->compare = Helpers::operator($operator);
+ }
+ $inst->setWhereData($value, $column, $inst->where);
+ $inst->set[] = (string)$value;
+ return $inst;
+ }
+
+ /**
+ * Create protected MySQL HAVING input
+ * @param string|AttrInterface $column Mysql column
+ * @param string|int|float|AttrInterface $value Equals to value
+ * @param string|null $operator Change comparison operator from default "=".
+ * @return self
+ */
+ public function having(string|AttrInterface $column, string|int|float|AttrInterface $value, ?string $operator = null): self
+ {
+ $inst = clone $this;
+ if (!is_null($operator)) {
+ $inst->compare = Helpers::operator($operator);
+ }
+ $this->setWhereData($value, $column, $inst->having);
+ return $inst;
+ }
+
+ /**
+ * Set Mysql ORDER
+ * @param string|AttrInterface $column Mysql Column
+ * @param string $sort Mysql sort type. Only "ASC" OR "DESC" is allowed, anything else will become "ASC".
+ * @return self
+ */
+ public function order(string|AttrInterface $column, string $sort = "ASC"): self
+ {
+ // PREP AT BUILD
+ //$col = $this->prep($col, false);
+ /*
+ if (!is_null($this->migration) && !$this->migration->columns([(string)$col])) {
+ throw new DBValidationException($this->migration->getMessage(), 1);
+ }
+ */
+ $inst = clone $this;
+ $inst->order[] = [
+ "column" => $this->attr($column, Attr::COLUMN_TYPE),
+ "sort" => Helpers::orderSort($sort)
+ ];
+ return $inst;
+ }
+
+ /**
+ * Add a limit and maybe an offset
+ * @param int|AttrInterface $limit
+ * @param int|AttrInterface|null $offset
+ * @return $this
+ */
+ public function limit(int|AttrInterface $limit, null|int|AttrInterface $offset = null): self
+ {
+ $inst = clone $this;
+ $inst->limit = $this->attr($limit, Attr::VALUE_TYPE_NUM);
+ if (!is_null($offset)) {
+ $inst->offset($offset);
+ }
+ return $inst;
+ }
+
+ /**
+ * Add an offset (if limit is not set then it will automatically become "1").
+ * @param int|AttrInterface $offset
+ * @return $this
+ */
+ public function offset(int|AttrInterface $offset): self
+ {
+ $inst = clone $this;
+ $inst->offset = $this->attr($offset, Attr::VALUE_TYPE_NUM);
+ return $inst;
+ }
+
+
+ /**
+ * Add group
+ * @param array $columns
+ * @return self
+ */
+ public function group(...$columns): self
+ {
+ /*
+ if (!is_null($this->migration) && !$this->migration->columns($columns)) {
+ throw new DBValidationException($this->migration->getMessage(), 1);
+ }
+ */
+ $inst = clone $this;
+ $inst->group = $columns;
+ return $inst;
+ }
+
+ /**
+ * Add make query a distinct call
+ * @return self
+ */
+ public function distinct(): self
+ {
+ $inst = clone $this;
+ $inst->distinct = true;
+ return $inst;
+ }
+
+ /**
+ * Postgre specific function
+ * @param string $column
+ * @return $this
+ */
+ public function returning(string $column): self
+ {
+ $inst = clone $this;
+ $inst->returning = $column;
+ return $inst;
+ }
+
+ /**
+ * Mysql JOIN query (Default: INNER)
+ * @param string|array|MigrateInterface $table Mysql table name (if array e.g. [TABLE_NAME, ALIAS]) or MigrateInterface instance
+ * @param string|array|null $where Where data (as array or string e.g. string is raw)
+ * @param array $sprint Use sprint to prep data
+ * @param string $type Type of join
+ * @return self
+ */
+ public function join(
+ string|array|MigrateInterface $table,
+ string|array $where = null,
+ array $sprint = [],
+ string $type = "INNER"
+ ): self {
+
+ $inst = clone $this;
+ if ($table instanceof MigrateInterface) {
+ die("FIX");
+
+ } else {
+
+ /*
+ * if (is_null($where)) {
+ throw new ResultException("You need to specify the argument 2 (where) value!", 1);
+ }
+ */
+
+ // Try to move this to the start of the method
+ $tableInst = clone $inst;
+ $tableInst->alias = null;
+ $tableInst = $tableInst->table($table);
+
+ $data = [];
+ if (is_array($where)) {
+ foreach ($where as $key => $val) {
+ if (is_array($val)) {
+ foreach ($val as $grpKey => $grpVal) {
+ $inst->setWhereData($this->attr($grpVal, Attr::COLUMN_TYPE), $grpKey, $data);
+ }
+ } else {
+ $inst->setWhereData($this->attr($val, Attr::COLUMN_TYPE), $key, $data);
+ }
+ }
+ }
+ $type = Helpers::joinTypes(strtoupper($type)); // Whitelist
+
+ $inst->join[] = [
+ "type" => $type,
+ "table" => $tableInst->table,
+ "alias" => $tableInst->alias,
+ "where" => $where,
+ "whereData" => $data,
+ "sprint" => $sprint
+ ];
+ }
+ return $inst;
+ }
+
+ /**
+ * Union result
+ * @param DBInterface $inst
+ * @param bool $allowDuplicate UNION by default selects only distinct values.
+ * Use UNION ALL to also select duplicate values!
+ * @mixin AbstractDB
+ * @return self
+ */
+ public function union(DBInterface|string $dbInst, bool $allowDuplicate = false): self
+ {
+ $inst = clone $this;
+
+
+ if(!is_null($inst->order)) {
+ throw new \RuntimeException("You need to move your ORDER BY to the last UNION statement!");
+ }
+
+ if(!is_null($inst->limit)) {
+ throw new \RuntimeException("You need to move your ORDER BY to the last UNION statement!");
+ }
+
+ $inst->union[] = [
+ 'inst' => $dbInst,
+ 'allowDuplicate' => $allowDuplicate
+ ];
+ return $inst;
+ }
+
+ public function prepare(): self
+ {
+ $inst = clone $this;
+ $this->prepare = true;
+ return $this;
+ }
+
+
+ public function sql(): string
+ {
+ $this->builder = new QueryBuilder($this);
+ $sql = $this->builder->sql();
+ return $sql;
+ }
+
+ /**
+ * Propagate where data structure
+ * @param string|AttrInterface $key
+ * @param string|int|float|AttrInterface $val
+ * @param array|null &$data static value
+ */
+ final protected function setWhereData(string|int|float|AttrInterface $val, string|AttrInterface $key, ?array &$data): void
+ {
+ if (is_null($data)) {
+ $data = [];
+ }
+ /*
+ $key = (string)$this->prep($key, false);
+ $val = $this->prep($val);
+ if (!is_null($this->migration) && !$this->migration->where($key, $val)) {
+ throw new DBValidationException($this->migration->getMessage(), 1);
+ }
+ */
+
+ $data[$this->whereIndex][$this->whereAnd][$key][] = [
+ "column" => $this->attr($key, Attr::COLUMN_TYPE),
+ "not" => $this->whereNot,
+ "operator" => $this->compare,
+ "value" => $this->attr($val, Attr::VALUE_TYPE)
+ ];
+
+ $this->resetWhere();
+ }
+
+
+ /**
+ * Group mysql WHERE inputs
+ * @param callable $call Every method where placed inside callback will be grouped.
+ * @return self
+ */
+ public function whereBind(callable $call): self
+ {
+ $inst = clone $this;
+ if (!is_null($inst->where)) {
+ $inst->whereIndex++;
+ }
+ $inst->resetWhere();
+ $call($inst);
+ $inst->whereIndex++;
+ return $inst;
+ }
+
+
+ /**
+ * Will reset Where input
+ * No need to clone as this will return void
+ * @return void
+ */
+ protected function resetWhere(): void
+ {
+ $this->whereNot = false;
+ $this->whereAnd = "AND";
+ $this->compare = "=";
+ }
+
+
+ /**
+ * Query result
+ * @param string|self $sql
+ * @param string|null $method
+ * @param array $args
+ * @return array|object|bool|string
+ * @throws ResultException
+ */
+ final public function query(string|self $sql, ?string $method = null, array $args = []): array|object|bool|string
+ {
+ $query = new Query($this->connection, $sql);
+ $query->setPluck($this->pluck);
+ if (!is_null($method)) {
+ if (method_exists($query, $method)) {
+ return call_user_func_array([$query, $method], $args);
+ }
+ throw new ResultException("Method \"$method\" does not exists!", 1);
+ }
+ return $query;
+ }
+
+ /**
+ * Execute
+ * @return mixed
+ * @throws ResultException
+ */
+ function execute()
+ {
+ if(is_null($this->result)) {
+ $this->result = $this->query($this->sql())->execute();
+ }
+ return $this->result;
+ }
+
+
+ /**
+ MIGRATION BUILDERS
+ */
+
+ /**
+ * Build join data from Migrate data
+ * @param MigrateInterface $mig
+ * @param string $type Join type (INNER, LEFT, ...)
+ * @return array
+ * @throws ConnectException
+ */
+ final protected function buildJoinFromMig(MigrateInterface $mig, string $type): array
+ {
+ $joinArr = [];
+ $prefix = $this->connInst()->getHandler()->getPrefix();
+ $main = $this->getMainFKData();
+ $data = $mig->getData();
+ $this->migration->mergeData($data);
+ $migTable = $mig->getTable();
+
+ foreach ($data as $col => $row) {
+ if (isset($row['fk'])) {
+ foreach ($row['fk'] as $a) {
+ if ($a['table'] === (string)$this->table) {
+ $joinArr[] = "$type JOIN " . $prefix . $migTable . " " . $migTable .
+ " ON (" . $migTable . ".$col = {$a['table']}.{$a['column']})";
+ }
+ }
+ } else {
+ foreach ($main as $c => $a) {
+ foreach ($a as $t => $d) {
+ if (in_array($col, $d)) {
+ $joinArr[] = "$type JOIN " . $prefix . $migTable . " " . $migTable .
+ " ON ($t.$col = $this->alias.$c)";
+ }
+ }
+ }
+ }
+ }
+ return $joinArr;
+ }
+
+ /**
+ * Get the Main FK data protocol
+ * @return array
+ */
+ final protected function getMainFKData(): array
+ {
+ if (is_null($this->fkData)) {
+ $this->fkData = [];
+ foreach ($this->mig->getMig()->getData() as $col => $row) {
+ if (isset($row['fk'])) {
+ foreach ($row['fk'] as $a) {
+ $this->fkData[$col][$a['table']][] = $a['column'];
+ }
+ }
+ }
+ }
+ return $this->fkData;
+ }
+
+}
diff --git a/Exceptions/DBQueryException.php b/Exceptions/ResultException.php
similarity index 62%
rename from Exceptions/DBQueryException.php
rename to Exceptions/ResultException.php
index 1ffc502..69e4d6f 100755
--- a/Exceptions/DBQueryException.php
+++ b/Exceptions/ResultException.php
@@ -5,10 +5,10 @@
use Exception;
/**
- * Class DBQueryException
+ * Class ResultException
*
* @package MaplePHP\Query\Exceptions
*/
-class DBQueryException extends Exception
+class ResultException extends Exception
{
}
diff --git a/Handlers/MySQL/MySQLConnect.php b/Handlers/MySQL/MySQLConnect.php
new file mode 100644
index 0000000..5ab5632
--- /dev/null
+++ b/Handlers/MySQL/MySQLConnect.php
@@ -0,0 +1,94 @@
+getMessage(), $e->getCode(), $e);
+ }
+ }
+
+ /**
+ * Access the database main class
+ * @param string $method
+ * @param array $arguments
+ * @return object|false
+ */
+ public function __call(string $method, array $arguments): object|false
+ {
+ return call_user_func_array([$this, $method], $arguments);
+ }
+
+ /**
+ * Performs a query on the database
+ * https://www.php.net/manual/en/mysqli.query.php
+ * @param string $query
+ * @param int $result_mode
+ * @return mysqli_result|bool
+ */
+ public function query(string $query, int $result_mode = MYSQLI_STORE_RESULT): mysqli_result|bool
+ {
+ return parent::query($query, $result_mode);
+ }
+
+ /**
+ * Performs a query on the database
+ * https://www.php.net/manual/en/mysqli.query.php
+ * @param string $query
+ * @return mysqli_result|bool
+ */
+ public function prepare(string $query): mysqli_stmt|false
+ {
+ return parent::prepare($query);
+ }
+
+ /**
+ * Returns the value generated for an AI column by the last query
+ * @param string|null $column Is only used with PostgreSQL!
+ * @return int
+ */
+ public function insert_id(?string $column = null): int
+ {
+ return $this->insert_id;
+ }
+
+ /**
+ * Close connection
+ * @return bool
+ */
+ public function close(): true
+ {
+ return $this->close();
+ }
+
+ /**
+ * Prep value / SQL escape string
+ * @param string $value
+ * @return string
+ */
+ public function prep(string $value): string
+ {
+ return $this->real_escape_string($value);
+ }
+}
diff --git a/Handlers/MySQLHandler.php b/Handlers/MySQLHandler.php
index ee25f28..dc89217 100755
--- a/Handlers/MySQLHandler.php
+++ b/Handlers/MySQLHandler.php
@@ -1,10 +1,13 @@
connection = new mysqli($this->server, $this->user, $this->pass, $this->dbname, $this->port);
+ $this->connection = new MySQLConnect($this->server, $this->user, $this->pass, $this->dbname, $this->port);
if (mysqli_connect_error()) {
throw new ConnectException('Failed to connect to MySQL: ' . mysqli_connect_error(), 1);
}
@@ -153,7 +156,7 @@ public function prep(string $value): string
public function multiQuery(string $sql, object &$db = null): array
{
$count = 0;
- $err = array();
+ $err = [];
$db = $this->connection;
if (mysqli_multi_query($db, $sql)) {
do {
@@ -175,15 +178,4 @@ public function multiQuery(string $sql, object &$db = null): array
}
return $err;
}
-
- /**
- * Start Transaction
- * @return mysqli
- */
- public function transaction(): mysqli
- {
- $this->connection->begin_transaction();
- return $this->connection;
- }
-
}
diff --git a/Handlers/PostgreSQL/PostgreSQLConnect.php b/Handlers/PostgreSQL/PostgreSQLConnect.php
index bc3fc4a..9e1489c 100755
--- a/Handlers/PostgreSQL/PostgreSQLConnect.php
+++ b/Handlers/PostgreSQL/PostgreSQLConnect.php
@@ -1,60 +1,106 @@
connection = pg_connect("host=$server port=$port dbname=$dbname user=$user password=$pass");
- if (!$this->connection) {
- $this->error = pg_last_error();
+
+ try {
+ $this->connection = pg_connect("host=$server port=$port dbname=$dbname user=$user password=$pass");
+ if (!is_null($this->connection)) {
+ $this->error = pg_last_error($this->connection);
+ }
+ } catch (Exception $e) {
+ throw new ConnectException('Failed to connect to PostgreSQL: ' . $e->getMessage(), $e->getCode(), $e);
}
+
+ $this->key = "postgre_query_" . self::$index;
+ self::$index++;
+
}
+ /**
+ * Get connection
+ * @return Connection
+ */
public function getConnection(): Connection
{
return $this->connection;
}
+ /**
+ * Make a prepare statement
+ * @param string $query
+ * @return StmtInterface|false
+ */
+ public function prepare(string $query): StmtInterface|false
+ {
+ $index = 1;
+ $query = preg_replace_callback('/\?/', function() use(&$index) {
+ return '$' . $index++;
+ }, $query);
+
+
+ if (pg_prepare($this->connection, $this->key, $query)) {
+ return new PostgreSQLStmt($this->connection, $this->key);
+ }
+ return false;
+ }
+
/**
* Returns Connection of PgSql\Connection
- * @param string $name
+ * @param string $method
* @param array $arguments
* @return Connection|false
*/
- public function __call(string $name, array $arguments): Connection|false
+ public function __call(string $method, array $arguments): Connection|false
{
- return call_user_func_array([$this->connection, $name], $arguments);
+ return call_user_func_array([$this->connection, $method], $arguments);
}
/**
* Query sql
- * @param $sql
+ * @param $query
+ * @param int $result_mode
* @return PostgreSQLResult|bool
*/
- function query($sql): PostgreSQLResult|bool
+ public function query($query, int $result_mode = 0): PostgreSQLResult|bool
{
if($this->connection instanceof Connection) {
$this->query = new PostgreSQLResult($this->connection);
- if($query = $this->query->query($sql)) {
+ if($query = $this->query->query($query)) {
return $query;
}
- $this->error = pg_result_error($this->connection);
+ $this->error = pg_result_error($this->query);
}
return false;
}
@@ -63,7 +109,7 @@ function query($sql): PostgreSQLResult|bool
* Begin transaction
* @return bool
*/
- function begin_transaction(): bool
+ public function begin_transaction(): bool
{
return (bool)$this->query("BEGIN");
}
@@ -72,7 +118,7 @@ function begin_transaction(): bool
* Commit transaction
* @return bool
*/
- function commit(): bool
+ public function commit(): bool
{
return (bool)$this->query("COMMIT");
}
@@ -81,7 +127,7 @@ function commit(): bool
* Rollback transaction
* @return bool
*/
- function rollback(): bool
+ public function rollback(): bool
{
return (bool)$this->query("ROLLBACK");
}
@@ -89,18 +135,33 @@ function rollback(): bool
/**
* Get insert ID
* @return mixed
+ * @throws ResultException
*/
- function insert_id(?string $column = null): int
+ public function insert_id(?string $column = null): int
{
+ if(is_null($column)) {
+ throw new ResultException("PostgreSQL expects a column name for a return result.");
+ }
return (int)pg_fetch_result($this->query, 0, $column);
}
/**
* Close the connection
- * @return void
+ * @return true
*/
- function close(): void
+ public function close(): true
{
pg_close($this->connection);
+ return true;
+ }
+
+ /**
+ * Prep value / SQL escape string
+ * @param string $value
+ * @return string
+ */
+ public function prep(string $value): string
+ {
+ return pg_escape_string($this->connection, $value);
}
}
diff --git a/Handlers/PostgreSQL/PostgreSQLResult.php b/Handlers/PostgreSQL/PostgreSQLResult.php
index ecfd402..cceb15b 100755
--- a/Handlers/PostgreSQL/PostgreSQLResult.php
+++ b/Handlers/PostgreSQL/PostgreSQLResult.php
@@ -1,4 +1,5 @@
connection = $connection;
+ $this->connection = $connection;
+ if(!is_null($query)) {
+ $this->query = $query;
+ $this->num_rows = pg_affected_rows($this->query);
+ }
}
/**
diff --git a/Handlers/PostgreSQL/PostgreSQLStmt.php b/Handlers/PostgreSQL/PostgreSQLStmt.php
new file mode 100755
index 0000000..19a944d
--- /dev/null
+++ b/Handlers/PostgreSQL/PostgreSQLStmt.php
@@ -0,0 +1,78 @@
+connection = $connection;
+ $this->key = $key;
+ }
+
+ /**
+ * Binds variables to a prepared statement as parameters
+ * https://www.php.net/manual/en/mysqli-stmt.bind-param.php
+ * @param string $types
+ * @param mixed $var
+ * @param mixed ...$vars
+ * @return bool
+ */
+ public function bind_param(string $types, mixed &$var, mixed &...$vars): bool
+ {
+ $params = array_merge([$var], $vars);
+ $this->result = pg_execute($this->connection, $this->key, $params);
+ $this->success = ($this->result !== false);
+ return $this->success;
+ }
+
+ /**
+ * Executes a prepared statement
+ * Not really needed in PostgreSQL but added as a placeholder
+ * https://www.php.net/manual/en/mysqli-stmt.execute.php
+ * @return bool
+ */
+ public function execute(): bool
+ {
+ return $this->success;
+ }
+
+ /**
+ * Gets a result set from a prepared statement as a ResultInterface object
+ * https://www.php.net/manual/en/mysqli-stmt.get-result.php
+ * @return ResultInterface
+ */
+ public function get_result(): ResultInterface
+ {
+ return new PostgreSQLResult($this->connection, $this->result);
+ }
+
+ /**
+ * Closes a prepared statement
+ * https://www.php.net/manual/en/mysqli-stmt.close.php
+ * @return true
+ */
+ public function close(): true
+ {
+ pg_query($this->connection, "DEALLOCATE $this->key");
+ return true;
+ }
+
+}
diff --git a/Handlers/PostgreSQLHandler.php b/Handlers/PostgreSQLHandler.php
index 0718f24..c8056a0 100755
--- a/Handlers/PostgreSQLHandler.php
+++ b/Handlers/PostgreSQLHandler.php
@@ -1,14 +1,15 @@
connection instanceof Connection);
+ return ($this->connection instanceof PostgreSQLConnect);
}
/**
* Connect to database
- * @return PostgreSQLConnect
+ * @return ConnectInterface
* @throws ConnectException
*/
- public function execute(): PostgreSQLConnect
+ public function execute(): ConnectInterface
{
-
$this->connection = new PostgreSQLConnect($this->server, $this->user, $this->pass, $this->dbname, $this->port);
- if (!is_null($this->connection->error)) {
+ if (!empty($this->connection->error)) {
throw new ConnectException('Failed to connect to PostgreSQL: ' . $this->connection->error, 1);
}
$encoded = pg_set_client_encoding($this->connection->getConnection(), $this->charset);
@@ -151,16 +151,6 @@ public function prep(string $value): string
return pg_escape_string($this->connection->getConnection(), $value);
}
- /**
- * Start Transaction
- * @return PostgreSQLConnect
- */
- public function transaction(): PostgreSQLConnect
- {
- $this->connection->begin_transaction();
- return $this->connection;
- }
-
/**
* Execute multiple queries at once (e.g. from a sql file)
* @param string $sql
@@ -170,7 +160,7 @@ public function transaction(): PostgreSQLConnect
public function multiQuery(string $sql, object &$db = null): array
{
$count = 0;
- $err = array();
+ $err = [];
$db = $this->connection->getConnection();
// Split the SQL string into individual queries
$queries = explode(';', $sql);
diff --git a/Handlers/SQLite/SQLiteConnect.php b/Handlers/SQLite/SQLiteConnect.php
index 3c1f0f1..79eacb4 100644
--- a/Handlers/SQLite/SQLiteConnect.php
+++ b/Handlers/SQLite/SQLiteConnect.php
@@ -5,27 +5,27 @@
use Exception;
use MaplePHP\Query\Exceptions\ConnectException;
use MaplePHP\Query\Interfaces\ConnectInterface;
+use MaplePHP\Query\Interfaces\StmtInterface;
use SQLite3;
-use SQLite3Result;
class SQLiteConnect implements ConnectInterface
{
-
- public string|int $insert_id;
- public string $error;
+ public string $error = "";
private SQLiteResult $query;
private SQLite3 $connection;
+ /**
+ * @throws ConnectException
+ */
public function __construct(string $database)
{
try {
$this->connection = new SQLite3($database);
} catch (Exception $e) {
- throw new ConnectException('Failed to connect to SQLite: ' . $e->getMessage(), 1);
+ throw new ConnectException('Failed to connect to SQLite: ' . $e->getMessage(), $e->getCode(), $e);
}
- return $this->connection;
}
/**
@@ -42,17 +42,29 @@ public function __call(string $method, array $arguments): SQLite3|false
/**
* Performs a query on the database
* @param string $query
+ * @param int $result_mode
* @return object|false
*/
- public function query(string $query): SQLiteResult|false
+ public function query(string $query, int $result_mode = 0): SQLiteResult|false
{
$result = new SQLiteResult($this->connection);
if($this->query = $result->query($query)) {
return $this->query;
}
$this->error = $this->connection->lastErrorMsg();
+ return false;
+ }
- //$this->query = parent::query($query);
+ /**
+ * Make a prepare statement
+ * @param string $query
+ * @return StmtInterface|false
+ */
+ public function prepare(string $query): StmtInterface|false
+ {
+ if ($stmt = $this->connection->prepare($query)) {
+ return new SQLiteStmt($this->connection, $stmt);
+ }
return false;
}
@@ -60,7 +72,7 @@ public function query(string $query): SQLiteResult|false
* Begin transaction
* @return bool
*/
- function begin_transaction(): bool
+ public function begin_transaction(): bool
{
return (bool)$this->query("BEGIN TRANSACTION");
}
@@ -69,7 +81,7 @@ function begin_transaction(): bool
* Commit transaction
* @return bool
*/
- function commit(): bool
+ public function commit(): bool
{
return (bool)$this->query("COMMIT");
}
@@ -78,7 +90,7 @@ function commit(): bool
* Rollback transaction
* @return bool
*/
- function rollback(): bool
+ public function rollback(): bool
{
return (bool)$this->query("ROLLBACK");
}
@@ -88,9 +100,28 @@ function rollback(): bool
* @param string|null $column Is only used with PostgreSQL!
* @return int
*/
- function insert_id(?string $column = null): int
+ public function insert_id(?string $column = null): int
{
return $this->connection->lastInsertRowID();
}
-}
\ No newline at end of file
+ /**
+ * Close connection
+ * @return bool
+ */
+ public function close(): true
+ {
+ return true;
+ }
+
+ /**
+ * Prep value / SQL escape string
+ * @param string $value
+ * @return string
+ */
+ public function prep(string $value): string
+ {
+ return SQLite3::escapeString($value);
+ }
+
+}
diff --git a/Handlers/SQLite/SQLiteResult.php b/Handlers/SQLite/SQLiteResult.php
index 3eacd81..b165a74 100644
--- a/Handlers/SQLite/SQLiteResult.php
+++ b/Handlers/SQLite/SQLiteResult.php
@@ -1,28 +1,31 @@
connection = $connection;
+ $this->query = $query;
+ if($this->query !== false) {
+ $this->preFetchData();
+ }
}
/**
@@ -56,7 +59,6 @@ public function fetch_object(string $class = "stdClass", array $constructor_args
$data = $this->bindToClass($data, $class, $constructor_args);
}
$this->endIndex();
-
return $data;
}
@@ -137,7 +139,7 @@ protected function preFetchData(): void
{
$this->rowsObj = $this->rows = [];
$this->num_rows = 0;
- $obj = $arr = array();
+ $obj = $arr = [];
while ($row = $this->query->fetchArray(SQLITE3_ASSOC)) {
$arr[] = $row;
$obj[] = (object)$row;
@@ -182,7 +184,7 @@ protected function endIndex(): void
* @return object|string|null
* @throws ReflectionException
*/
- protected function bindToClass(object|array $data, string $class, array $constructor_args = []): object|string|null
+ final protected function bindToClass(object|array $data, string $class, array $constructor_args = []): object|string|null
{
$reflection = new ReflectionClass($class);
$object = $reflection->newInstanceArgs($constructor_args);
diff --git a/Handlers/SQLite/SQLiteStmt.php b/Handlers/SQLite/SQLiteStmt.php
new file mode 100755
index 0000000..4c7b578
--- /dev/null
+++ b/Handlers/SQLite/SQLiteStmt.php
@@ -0,0 +1,83 @@
+connection = $connection;
+ $this->stmt = $stmt;
+ }
+
+ /**
+ * Binds variables to a prepared statement as parameters
+ * https://www.php.net/manual/en/mysqli-stmt.bind-param.php
+ * @param string $types
+ * @param mixed $var
+ * @param mixed ...$vars
+ * @return bool
+ */
+ public function bind_param(string $types, mixed &$var, mixed &...$vars): bool
+ {
+ $params = array_merge([$var], $vars);
+ foreach($params as $key => $value) {
+ if(!$this->stmt->bindValue(($key + 1), $params[0], SQLITE3_TEXT)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Executes a prepared statement
+ * Not really needed in PostgreSQL but added as a placeholder
+ * https://www.php.net/manual/en/mysqli-stmt.execute.php
+ * @return bool
+ */
+ public function execute(): bool
+ {
+ $this->result = $this->stmt->execute();
+ return ($this->result !== false);
+ }
+
+ /**
+ * Gets a result set from a prepared statement as a ResultInterface object
+ * https://www.php.net/manual/en/mysqli-stmt.get-result.php
+ * @return ResultInterface
+ */
+ public function get_result(): ResultInterface
+ {
+ return new SQLiteResult($this->connection, $this->result);
+ }
+
+ /**
+ * Closes a prepared statement
+ * https://www.php.net/manual/en/mysqli-stmt.close.php
+ * @return true
+ */
+ public function close(): true
+ {
+ $this->stmt->close();
+ return true;
+ }
+
+}
diff --git a/Handlers/SQLiteHandler.php b/Handlers/SQLiteHandler.php
index 5cb96a0..de27401 100755
--- a/Handlers/SQLiteHandler.php
+++ b/Handlers/SQLiteHandler.php
@@ -1,4 +1,5 @@
connection)) {
- $result = $this->connection->querySingle('PRAGMA quick_check');
- return $result === 'ok';
+ $result = $this->connection->query('PRAGMA quick_check');
+ $obj = $result->fetch_object();
+ return ($obj->quick_check ?? "") === 'ok';
}
return false;
}
@@ -72,13 +76,13 @@ public function hasConnection(): bool
* @return SQLiteConnect
* @throws ConnectException
*/
- public function execute(): SQLiteConnect
+ public function execute(): ConnectInterface
{
try {
$this->connection = new SQLiteConnect($this->database);
-
+
} catch (Exception $e) {
- throw new ConnectException('Failed to connect to SQLite: ' . $e->getMessage(), 1);
+ throw new ConnectException('Failed to connect to SQLite: ' . $e->getMessage(), $e->getCode(), $e);
}
return $this->connection;
}
@@ -126,7 +130,6 @@ public function query(string $sql): bool|SQLiteResult
*/
public function close(): void
{
- $this->connection->close();
}
/**
@@ -136,7 +139,7 @@ public function close(): void
*/
public function prep(string $value): string
{
- return SQLiteConnect::escapeString($value);
+ return SQLite3::escapeString($value);
}
/**
@@ -148,7 +151,7 @@ public function prep(string $value): string
public function multiQuery(string $sql, object &$db = null): array
{
$count = 0;
- $err = array();
+ $err = [];
$queries = explode(";", $sql);
$db = $this->connection;
foreach ($queries as $query) {
@@ -164,14 +167,4 @@ public function multiQuery(string $sql, object &$db = null): array
}
return $err;
}
-
- /**
- * Start Transaction
- * @return SQLiteConnect
- */
- public function transaction(): SQLiteConnect
- {
- $this->connection->begin_transaction();
- return $this->connection;
- }
}
diff --git a/Interfaces/AttrInterface.php b/Interfaces/AttrInterface.php
index b261383..2e53320 100755
--- a/Interfaces/AttrInterface.php
+++ b/Interfaces/AttrInterface.php
@@ -16,13 +16,6 @@ public function __toString();
*/
public function getRaw(): string|array;
- /**
- * Initiate the instance
- * @param string $value
- * @return self
- */
- public static function value(array|string|int|float $value): self;
-
/**
* Enable/disable MySQL prep
* @param bool $prep
diff --git a/Interfaces/ConnectInterface.php b/Interfaces/ConnectInterface.php
index 3097eac..ddf27d8 100644
--- a/Interfaces/ConnectInterface.php
+++ b/Interfaces/ConnectInterface.php
@@ -4,7 +4,6 @@
interface ConnectInterface
{
-
/**
* Access the database main class
* @param string $method
@@ -16,29 +15,30 @@ public function __call(string $method, array $arguments): object|false;
/**
* Performs a query on the database
* @param string $query
- * @return object|false
+ * @param int $result_mode If database
+ * @return mixed
*/
- public function query(string $query): object|bool;
+ public function query(string $query, int $result_mode = 0): mixed;
/**
* Begin transaction
* @return bool
*/
- function begin_transaction(): bool;
+ public function begin_transaction(): bool;
/**
* Commit transaction
* @return bool
*/
- function commit(): bool;
+ public function commit(): bool;
/**
* Rollback transaction
* @return bool
*/
- function rollback(): bool;
+ public function rollback(): bool;
/**
@@ -46,5 +46,18 @@ function rollback(): bool;
* @param string|null $column Is only used with PostgreSQL!
* @return int
*/
- function insert_id(?string $column = null): int;
-}
\ No newline at end of file
+ public function insert_id(?string $column = null): int;
+
+ /**
+ * Close connection
+ * @return bool
+ */
+ public function close(): true;
+
+ /**
+ * Prep value / SQL escape string
+ * @param string $value
+ * @return string
+ */
+ public function prep(string $value): string;
+}
diff --git a/Interfaces/DBInterface.php b/Interfaces/DBInterface.php
index 98bbc32..b15b647 100755
--- a/Interfaces/DBInterface.php
+++ b/Interfaces/DBInterface.php
@@ -7,194 +7,15 @@
namespace MaplePHP\Query\Interfaces;
+/**
+ * @method bind(\MaplePHP\Query\Prepare $param, array $statements)
+ */
interface DBInterface
{
/**
- * Change where compare operator from default "=".
- * Will change back to default after where method is triggered
- * @param string $operator once of (">", ">=", "<", "<>", "!=", "<=", "<=>")
- * @return self
- */
- public function compare(string $operator): self;
-
- /**
- * Chaining where with mysql "AND" or with "OR"
- * @return self
- */
- public function and(): self;
-
- /**
- * Chaining where with mysql "AND" or with "OR"
- * @return self
- */
- public function or(): self;
-
- /**
- * Access Query Attr class
- * @param array|string|int|float $value
- * @return AttrInterface
- */
- public static function withAttr(array|string|int|float $value, ?array $args = null): AttrInterface;
-
- /**
- * Raw Mysql Where input
- * Uses vsprintf to mysql prep/protect input in string. Prep string values needs to be eclosed manually
- * @param string $sql SQL string example: (id = %d AND permalink = '%s')
- * @param array $arr Mysql prep values
- * @return self
- */
- public function whereRaw(string $sql, ...$arr): self;
-
- /**
- * Create protected MySQL WHERE input
- * Supports dynamic method name calls like: whereIdStatus(1, 0)
- * @param string $key Mysql column
- * @param string|int|float|AttrInterface $val Equals to value
- * @param string|null $operator Change comparison operator from default "=".
- * @return self
- */
- public function where(string|AttrInterface $key, string|int|float|AttrInterface $val, ?string $operator = null): self;
-
-
- /**
- * Group mysql WHERE inputs
- * @param callable $call Evere method where placed inside callback will be grouped.
- * @return self
- */
- public function whereBind(callable $call): self;
-
- /**
- * Create protected MySQL HAVING input
- * @param string $key Mysql column
- * @param string|int|float|AttrInterface $val Equals to value
- * @param string|null $operator Change comparison operator from default "=".
- * @return self
- */
- public function having(string|AttrInterface $key, string|int|float|AttrInterface $val, ?string $operator = null): self;
-
- /**
- * Raw Mysql HAVING input
- * Uses vsprintf to mysql prep/protect input in string. Prep string values needs to be eclosed manually
- * @param string $sql SQL string example: (id = %d AND permalink = '%s')
- * @param array $arr Mysql prep values
- * @return self
- */
- public function havingRaw(string $sql, ...$arr): self;
-
- /**
- * Add a limit and maybee a offset
- * @param int $limit
- * @param int|null $offset
- * @return self
- */
- public function limit(int $limit, ?int $offset = null): self;
-
-
- /**
- * Add a offset (if limit is not set then it will automatically become "1").
- * @param int $offset
- * @return self
- */
- public function offset(int $offset): self;
-
- /**
- * Set Mysql ORDER
- * @param string $col Mysql Column
- * @param string $sort Mysql sort type. Only "ASC" OR "DESC" is allowed, anything else will become "ASC".
- * @return self
- */
- public function order(string|AttrInterface $col, string $sort = "ASC"): self;
-
- /**
- * Raw Mysql ORDER input
- * Uses vsprintf to mysql prep/protect input in string. Prep string values needs to be eclosed manually
- * @param string $sql SQL string example: (id ASC, parent DESC)
- * @param array $arr Mysql prep values
- * @return self
- */
- public function orderRaw(string $sql, ...$arr): self;
-
-
- /**
- * Add group
- * @param mixed $columns
- * @return self
- */
- public function group(...$columns): self;
-
-
- /**
- * Mysql JOIN query (Default: INNER)
- * @param string|array|MigrateInterface $table Mysql table name (if array e.g. [TABLE_NAME, ALIAS]) or MigrateInterface instance
- * @param array|string $where Where data (as array or string e.g. string is raw)
- * @param array $sprint Use sprint to prep data
- * @param string $type Type of join
- * @return self
- */
- public function join(string|array|MigrateInterface $table, string|array $where = null, array $sprint = array(), string $type = "INNER"): self;
-
-
- /**
- * Add make query a distinct call
- * @return self
- */
- public function distinct(): self;
-
-
- /**
- * Exaplain the mysql query. Will tell you how you can make improvements
- * @return self
- */
- public function explain(): self;
-
-
- /**
- * Create INSERT or UPDATE set Mysql input to insert
- * @param string|array|AttrInterface $key (string) "name" OR (array) ["id" => 1, "name" => "Lorem ipsum"]
- * @param string|array|AttrInterface $value If key is string then value will pair with key "Lorem ipsum"
- * @return self
- */
- public function set(string|array|AttrInterface $key, string|array|AttrInterface $value = null): self;
-
- /**
- * UPROTECTED: Create INSERT or UPDATE set Mysql input to insert
- * @param string $key Mysql column
- * @param string $value Input/insert value (UPROTECTED and Will not enclose)
- */
- public function setRaw(string $key, string $value): self;
-
-
- /**
- * Update if ID KEY is duplicate else insert
- * @param string|array $key (string) "name" OR (array) ["id" => 1, "name" => "Lorem ipsum"]
- * @param string|null $value If key is string then value will pair with key "Lorem ipsum"
- * @return self
- */
- public function onDupKey($key = null, ?string $value = null): self;
-
-
- /**
- * Union result
- * @param DBInterface $inst
- * @param bool $allowDuplicate UNION by default selects only distinct values.
- * Use UNION ALL to also select duplicate values!
- * @return self
- */
- public function union(DBInterface $inst, bool $allowDuplicate = false): self;
-
- /**
- * Union raw result, create union with raw SQL code
- * @param string $sql
- * @param bool $allowDuplicate UNION by default selects only distinct values.
- * Use UNION ALL to also select duplicate values!
- * @mixin AbstractDB
- * @return self
- */
- public function unionRaw(string $sql, bool $allowDuplicate = false): self;
-
- /**
- * Genrate SQL string of current instance/query
+ * Generate SQL string of current instance/query
* @return string
*/
public function sql(): string;
+
}
diff --git a/Interfaces/HandlerInterface.php b/Interfaces/HandlerInterface.php
index 068a189..1d6673d 100755
--- a/Interfaces/HandlerInterface.php
+++ b/Interfaces/HandlerInterface.php
@@ -36,10 +36,10 @@ public function hasConnection(): bool;
/**
* Connect to database
- * @return mixed
+ * @return ConnectInterface
* @throws ConnectException
*/
- public function execute(): mixed;
+ public function execute(): ConnectInterface;
/**
diff --git a/Interfaces/QueryBuilderInterface.php b/Interfaces/QueryBuilderInterface.php
new file mode 100755
index 0000000..5787db1
--- /dev/null
+++ b/Interfaces/QueryBuilderInterface.php
@@ -0,0 +1,12 @@
+query = $db;
+ $this->sql = $db->prepare()->sql();
+ $this->stmt = $db->getConnection()->prepare($this->sql);
+ }
+
+ /**
+ * Access DB -> and Query
+ * @param string $name
+ * @param array $arguments
+ * @return mixed
+ */
+ public function __call(string $name, array $arguments): mixed
+ {
+ $query = $this->prepExecute();
+ if(!method_exists($query, $name)) {
+ throw new \BadMethodCallException("The method '$name' does not exist in " . get_class($query) . ".");
+ }
+ return $query->$name(...$arguments);
+ }
+
+ /**
+ * @param DBInterface $db
+ * @return void
+ */
+ public function bind(DBInterface $db): void
+ {
+ $this->statements[0] = $db->prepare();
+ }
+
+ /**
+ * Combine multiple
+ * This is a test and might be removed in future
+ * @param DBInterface $db
+ * @return void
+ */
+ public function combine(DBInterface $db): void
+ {
+ $this->statements[] = $db->prepare();
+ }
+
+ /**
+ * Get SQL code
+ * @return string
+ */
+ public function sql(): string
+ {
+ return $this->sql;
+ }
+
+ /**
+ * Get STMT
+ * @return mixed
+ */
+ public function getStmt()
+ {
+ return $this->stmt;
+ }
+
+ /**
+ * Get bound keys
+ * @param int $length
+ * @return string
+ */
+ public function getKeys(int $length): string
+ {
+ if(is_null($this->keys)) {
+ $this->keys = str_pad("", $length, "s");
+ }
+ return $this->keys;
+ }
+
+ /**
+ * This will the rest of the library that it expects a prepended call
+ * @return Query
+ */
+ private function prepExecute(): Query
+ {
+ return $this->query->bind($this, $this->statements);
+ }
+
+ /**
+ * Execute
+ * @return array|bool|object
+ * @throws ConnectException
+ */
+ function execute(): object|bool|array
+ {
+ $query = $this->prepExecute();
+ return $query->execute();
+ }
+
+}
diff --git a/Query.php b/Query.php
index 603e5af..ed23513 100755
--- a/Query.php
+++ b/Query.php
@@ -1,24 +1,30 @@
sql = $sql;
if ($sql instanceof DBInterface) {
$this->sql = $sql->sql();
}
- $this->connection = is_null($connection) ? Connect::getInstance() : $connection;
+ $this->connection = $connection;
}
public function setPluck(?string $pluck): void
@@ -26,6 +32,13 @@ public function setPluck(?string $pluck): void
$this->pluck = $pluck;
}
+ public function bind($stmt, array $set): self
+ {
+ $this->stmt = $stmt;
+ $this->bind = $set;
+ return $this;
+ }
+
/**
* Execute query result
* @return object|array|bool
@@ -33,6 +46,10 @@ public function setPluck(?string $pluck): void
*/
public function execute(): object|array|bool
{
+ if(!is_null($this->bind)) {
+ return $this->executePrepare();
+ }
+
if ($result = $this->connection->query($this->sql)) {
return $result;
} else {
@@ -40,6 +57,27 @@ public function execute(): object|array|bool
}
}
+ /**
+ * Execute prepared query
+ * @return object|array|bool
+ */
+ public function executePrepare(): object|array|bool
+ {
+ if(is_null($this->bind)) {
+ throw new BadMethodCallException("You need to bind parameters first to execute a prepare statement!");
+ }
+ foreach ($this->bind as $bind) {
+ $ref = $bind->getQueryBuilder()->getSet();
+ $length = count($ref);
+ if($length > 0) {
+ $this->stmt->getStmt()->bind_param($this->stmt->getKeys($length), ...$ref);
+ }
+ $this->stmt->getStmt()->execute();
+ }
+ //$this->stmt->getStmt()->close();
+ return $this->stmt->getStmt()->get_result();
+ }
+
/**
* Execute query result And fetch as object
* @return bool|object|string
@@ -51,7 +89,7 @@ public function get(): bool|object|string
}
/**
- * SAME AS @get(): Execute query result And fetch as obejct
+ * SAME AS @get(): Execute query result And fetch as object
* @return bool|object|string (Mysql result)
* @throws ConnectException
*/
@@ -70,16 +108,39 @@ final public function obj(string $class = "stdClass", array $constructor_args =
/**
* Execute SELECT and fetch as array with nested objects
- * @param callable|null $callback callaback, make changes in query and if return then change key
+ * @param callable|null $callback callback, make changes in query and if return then change key
+ * @param string $class
+ * @param array $constructor_args
* @return array
* @throws ConnectException
*/
final public function fetch(?callable $callback = null, string $class = "stdClass", array $constructor_args = []): array
+ {
+ $arr = [];
+ $result = $this->execute();
+ if (is_array($result)) {
+ foreach($result as $resultItem) {
+ $arr = array_merge($arr, $this->fetchItem($resultItem, $callback, $class, $constructor_args));
+ }
+ } else {
+ $arr = $this->fetchItem($result, $callback, $class, $constructor_args);
+ }
+ return $arr;
+ }
+
+ /**
+ * fetch an item to be used in the main fetch method
+ * @param $result
+ * @param callable|null $callback
+ * @param string $class
+ * @param array $constructor_args
+ * @return array
+ */
+ protected function fetchItem($result, ?callable $callback = null, string $class = "stdClass", array $constructor_args = []): array
{
$key = 0;
$select = null;
- $arr = array();
- $result = $this->execute();
+ $arr = [];
if (is_object($result) && $result->num_rows > 0) {
while ($row = $result->fetch_object($class, $constructor_args)) {
@@ -93,14 +154,12 @@ final public function fetch(?callable $callback = null, string $class = "stdClas
$data = ((!is_null($select)) ? $select : $key);
if (is_array($data)) {
if (!is_array($select)) {
- throw new \InvalidArgumentException("The return value of the callable needs to be an array!", 1);
+ throw new InvalidArgumentException("The return value of the callable needs to be an array!", 1);
}
$arr = array_replace_recursive($arr, $select);
} else {
-
$arr[$data] = $row;
}
-
$key++;
}
}
diff --git a/QueryBuilder.php b/QueryBuilder.php
new file mode 100755
index 0000000..15d3c10
--- /dev/null
+++ b/QueryBuilder.php
@@ -0,0 +1,271 @@
+db = $sql;
+ }
+
+ public function __toString(): string
+ {
+ return $this->sql();
+ }
+
+ public function select(): string
+ {
+ $explain = $this->getExplain();
+ $noCache = $this->getNoCache();
+ $columns = $this->getColumns();
+ $distinct = $this->getDistinct();
+ $join = $this->getJoin();
+ $where = $this->getWhere("WHERE", $this->db->where);
+ $having = $this->getWhere("HAVING", $this->db->having);
+ $order = $this->getOrder();
+ $limit = $this->getLimit();
+ $group = $this->getGroup();
+ $union = $this->getUnion();
+
+ return "{$explain}SELECT $noCache$distinct$columns FROM " .
+ $this->getTable() . "$join$where$group$having$order$limit$union";
+ }
+
+ public function getTable(): string
+ {
+ return Helpers::addAlias($this->db->table, $this->db->alias);
+ }
+
+ /**
+ * Get sql code
+ * @return string
+ */
+ public function sql(): string
+ {
+ return $this->select();
+ }
+
+ /**
+ * Optimizing Queries with EXPLAIN
+ * @return string
+ */
+ protected function getExplain(): string
+ {
+ return ($this->db->explain) ? "EXPLAIN " : "";
+ }
+
+ /**
+ * The SELECT DISTINCT statement is used to return only distinct (different) values
+ * @return string
+ */
+ protected function getDistinct(): string
+ {
+ return ($this->db->distinct) ? "DISTINCT " : "";
+ }
+
+ /**
+ * The server does not use the query cache.
+ * @return string
+ */
+ protected function getNoCache(): string
+ {
+ return ($this->db->noCache) ? "SQL_NO_CACHE " : "";
+ }
+
+ /**
+ * The SELECT columns
+ * @return string
+ */
+ protected function getColumns(): string
+ {
+ if(is_null($this->db->columns)) {
+ return "*";
+ }
+ $create = [];
+ $columns = $this->db->columns;
+ foreach($columns as $row) {
+ $create[] = Helpers::addAlias($row['column'], $row['alias'], "AS");
+ }
+ return implode(",", $create);
+ }
+
+ /**
+ * Order rows by
+ * @return string
+ */
+ protected function getOrder(): string
+ {
+ return (!is_null($this->db->order)) ?
+ " ORDER BY " . implode(",", Helpers::getOrderBy($this->db->order)) : "";
+ }
+
+ /**
+ * The GROUP BY statement groups rows that have the same values into summary rows
+ * @return string
+ */
+ protected function getGroup(): string
+ {
+ return (!is_null($this->db->group)) ? " GROUP BY " . implode(",", $this->db->group) : "";
+ }
+
+ /**
+ * Will build where string
+ * @param string $prefix
+ * @param array|null $where
+ * @param array $set
+ * @return string
+ */
+ protected function getWhere(string $prefix, ?array $where, array &$set = []): string
+ {
+ $out = "";
+ if (!is_null($where)) {
+ $out = " $prefix";
+ $index = 0;
+ foreach ($where as $array) {
+ $firstAnd = key($array);
+ $out .= (($index > 0) ? " $firstAnd" : "") . " (";
+ $out .= $this->whereArrToStr($array, $set);
+ $out .= ")";
+ $index++;
+ }
+ }
+ return $out;
+ }
+
+ /**
+ * Build joins
+ * @return string
+ */
+ protected function getJoin(): string
+ {
+ $join = "";
+ $data = $this->db->join;
+ foreach ($data as $row) {
+ $table = Helpers::addAlias($row['table'], $row['alias']);
+ $where = $this->getWhere("ON", $row['whereData']);
+ $join .= " ". sprintf("%s JOIN %s%s", $row['type'], $table, $where);
+ }
+ return $join;
+ }
+
+ /**
+ * Build limit
+ * @return string
+ */
+ protected function getLimit(): string
+ {
+ $limit = $this->db->limit;
+ if (is_null($limit) && !is_null($this->db->offset)) {
+ $limit = 1;
+ }
+ $limit = $this->getAttrValue($limit);
+ $offset = (!is_null($this->db->offset)) ? "," . $this->getAttrValue($this->db->offset) : "";
+ return (!is_null($limit)) ? " LIMIT $limit $offset" : "";
+ }
+
+ /**
+ * Build Where data (CAN BE A HELPER?)
+ * @param array $array
+ * @param array $set
+ * @return string
+ */
+ private function whereArrToStr(array $array, array &$set = []): string
+ {
+ $out = "";
+ $count = 0;
+ foreach ($array as $key => $arr) {
+ foreach ($arr as $arrB) {
+ if (is_array($arrB)) {
+ foreach ($arrB as $row) {
+ if ($count > 0) {
+ $out .= "$key ";
+ }
+ if ($row['not'] === true) {
+ $out .= "NOT ";
+ }
+
+ $value = $this->getAttrValue($row['value']);
+ $out .= "{$row['column']} {$row['operator']} {$value} ";
+ $set[] = $row['value'];
+ $count++;
+ }
+
+ } else {
+ // Used to be used as RAW input but is not needed any more
+ die("DELETE???");
+ $out .= ($count) > 0 ? "$key $arrB " : $arrB;
+ $count++;
+ }
+ }
+ }
+ return rtrim($out, " ");
+ }
+
+
+ /**
+ * Get Union sql
+ * @return string
+ */
+ public function getUnion(): string
+ {
+ $union = $this->db->union;
+ if(!is_null($union)) {
+
+ $sql = "";
+ foreach($union as $row) {
+ $inst = new self($row['inst']);
+ $sql .= " UNION " . $inst->sql();
+ }
+
+ return $sql;
+ }
+ return "";
+ }
+
+ /**
+ * Get attribute as value item
+ * @param $value
+ * @return string|null
+ */
+ public function getAttrValue($value): ?string
+ {
+ if($this->db->prepare) {
+ if($value instanceof AttrInterface && ($value->isType(Attr::VALUE_TYPE) ||
+ $value->isType(Attr::VALUE_TYPE_NUM) || $value->isType(Attr::VALUE_TYPE_STR))) {
+ $this->set[] = $value->type(Attr::RAW_TYPE);
+ return "?";
+ }
+ }
+ return is_null($value) ? null : (string)$value;
+ }
+
+ /**
+ * Get set
+ * @return array
+ */
+ public function getSet(): array
+ {
+ return $this->set;
+ }
+
+}
diff --git a/QueryBuilderLegacy.php b/QueryBuilderLegacy.php
new file mode 100755
index 0000000..e161758
--- /dev/null
+++ b/QueryBuilderLegacy.php
@@ -0,0 +1,274 @@
+db = $sql;
+ }
+
+ public function __toString(): string
+ {
+ return $this->sql();
+ }
+
+ public function select(): string
+ {
+ $explain = $this->getExplain();
+ $noCache = $this->getNoCache();
+ $columns = $this->getColumns();
+ $distinct = $this->getDistinct();
+ $join = $this->getJoin();
+ $where = $this->getWhere("WHERE", $this->db->__get('where'));
+ $having = $this->getWhere("HAVING", $this->db->__get('having'));
+ $order = $this->getOrder();
+ $limit = $this->getLimit();
+ $group = $this->getGroup();
+ $union = $this->getUnion();
+
+ return "{$explain}SELECT $noCache$distinct$columns FROM " .
+ $this->getTable() . "$join$where$group$having$order$limit$union";
+ }
+
+ public function getTable(): string
+ {
+ return Helpers::addAlias($this->db->__get('table'), $this->db->__get('alias'));
+ }
+
+ /**
+ * Get sql code
+ * @return string
+ */
+ public function sql(): string
+ {
+ return $this->select();
+ }
+
+ /**
+ * Optimizing Queries with EXPLAIN
+ * @return string
+ */
+ protected function getExplain(): string
+ {
+ return ($this->db->__get('explain')) ? "EXPLAIN " : "";
+ }
+
+ /**
+ * The SELECT DISTINCT statement is used to return only distinct (different) values
+ * @return string
+ */
+ protected function getDistinct(): string
+ {
+ return ($this->db->__get('distinct')) ? "DISTINCT " : "";
+ }
+
+ /**
+ * The server does not use the query cache.
+ * @return string
+ */
+ protected function getNoCache(): string
+ {
+ return ($this->db->__get('noCache')) ? "SQL_NO_CACHE " : "";
+ }
+
+ /**
+ * The SELECT columns
+ * @return string
+ */
+ protected function getColumns(): string
+ {
+ if(is_null($this->db->__get('columns'))) {
+ return "*";
+ }
+ $create = [];
+ $columns = $this->db->__get('columns');
+ foreach($columns as $row) {
+ $create[] = Helpers::addAlias($row['column'], $row['alias'], "AS");
+ }
+ return implode(",", $create);
+ }
+
+ /**
+ * Order rows by
+ * @return string
+ */
+ protected function getOrder(): string
+ {
+ return (!is_null($this->db->__get('order'))) ?
+ " ORDER BY " . implode(",", Helpers::getOrderBy($this->db->__get('order'))) : "";
+ }
+
+ /**
+ * The GROUP BY statement groups rows that have the same values into summary rows
+ * @return string
+ */
+ protected function getGroup(): string
+ {
+ return (!is_null($this->db->__get('group'))) ? " GROUP BY " . implode(",", $this->db->__get('group')) : "";
+ }
+
+ /**
+ * Will build where string
+ * @param string $prefix
+ * @param array|null $where
+ * @param array $set
+ * @return string
+ */
+ protected function getWhere(string $prefix, ?array $where, array &$set = []): string
+ {
+ $out = "";
+ if (!is_null($where)) {
+ $out = " $prefix";
+ $index = 0;
+ foreach ($where as $array) {
+ $firstAnd = key($array);
+ $out .= (($index > 0) ? " $firstAnd" : "") . " (";
+ $out .= $this->whereArrToStr($array, $set);
+ $out .= ")";
+ $index++;
+ }
+ }
+ return $out;
+ }
+
+ /**
+ * Build joins
+ * @return string
+ */
+ protected function getJoin(): string
+ {
+ $join = "";
+ $data = $this->db->__get("join");
+ foreach ($data as $row) {
+ $table = Helpers::addAlias($row['table'], $row['alias']);
+ $where = $this->getWhere("ON", $row['whereData']);
+ $join .= " ". sprintf("%s JOIN %s%s", $row['type'], $table, $where);
+ }
+ return $join;
+ }
+
+ /**
+ * Build limit
+ * @return string
+ */
+ protected function getLimit(): string
+ {
+ $limit = $this->db->__get('limit');
+ if (is_null($limit) && !is_null($this->db->__get('offset'))) {
+ $limit = 1;
+ }
+ $limit = $this->getAttrValue($limit);
+ $offset = (!is_null($this->db->__get("offset"))) ? "," . $this->getAttrValue($this->db->__get("offset")) : "";
+ return (!is_null($limit)) ? " LIMIT $limit $offset" : "";
+ }
+
+ /**
+ * Build Where data (CAN BE A HELPER?)
+ * @param array $array
+ * @param array $set
+ * @return string
+ */
+ private function whereArrToStr(array $array, array &$set = []): string
+ {
+ $out = "";
+ $count = 0;
+ foreach ($array as $key => $arr) {
+ foreach ($arr as $arrB) {
+ if (is_array($arrB)) {
+ foreach ($arrB as $row) {
+ if ($count > 0) {
+ $out .= "$key ";
+ }
+ if ($row['not'] === true) {
+ $out .= "NOT ";
+ }
+
+ $value = $this->getAttrValue($row['value']);
+ $out .= "{$row['column']} {$row['operator']} {$value} ";
+ $set[] = $row['value'];
+ $count++;
+ }
+
+ } else {
+ // Used to be used as RAW input but is not needed any more
+ die("DELETE???");
+ $out .= ($count) > 0 ? "$key $arrB " : $arrB;
+ $count++;
+ }
+ }
+ }
+ return rtrim($out, " ");
+ }
+
+
+ /**
+ * Get Union sql
+ * @return string
+ */
+ public function getUnion(): string
+ {
+ $union = $this->db->__get('union');
+ if(!is_null($union)) {
+
+ $sql = "";
+ foreach($union as $row) {
+ $inst = new self($row['inst']);
+ $sql .= " UNION " . $inst->sql();
+ }
+
+ return $sql;
+ }
+ return "";
+ }
+
+ /**
+ * Get attribute as value item
+ * @param $value
+ * @return string|null
+ */
+ public function getAttrValue($value): ?string
+ {
+ if($this->db->__get('prepare')) {
+ if($value instanceof AttrInterface && ($value->isType(Attr::VALUE_TYPE) ||
+ $value->isType(Attr::VALUE_TYPE_NUM) || $value->isType(Attr::VALUE_TYPE_STR))) {
+ $this->set[] = $value->type(Attr::RAW_TYPE);
+ return "?";
+ }
+ }
+ return is_null($value) ? null : (string)$value;
+ }
+
+ /**
+ * Get set
+ * @return array
+ */
+ public function getSet(): array
+ {
+ if(!$this->db->__get('prepare')) {
+ throw new RuntimeException("Prepare method not available");
+ }
+ return $this->set;
+ }
+
+}
diff --git a/Utility/Attr.php b/Utility/Attr.php
index 3dbe363..04f87f5 100755
--- a/Utility/Attr.php
+++ b/Utility/Attr.php
@@ -2,43 +2,49 @@
namespace MaplePHP\Query\Utility;
-use MaplePHP\Query\Connect;
+use BadMethodCallException;
+use InvalidArgumentException;
+use MaplePHP\Query\Handlers\PostgreSQL\PostgreSQLConnect;
use MaplePHP\Query\Interfaces\AttrInterface;
use MaplePHP\DTO\Format\Encode;
+use MaplePHP\Query\Interfaces\ConnectInterface;
+use MaplePHP\Query\Interfaces\HandlerInterface;
+/**
+ * I might make it IMMUTABLE in future
+ */
class Attr implements AttrInterface
{
- private $value;
- private $raw;
- private $hasBeenEncoded = false;
- private $prep = true;
- private $enclose = true;
- private $jsonEncode = true;
- private $encode = false;
+ public const RAW_TYPE = 0;
+ public const VALUE_TYPE = 1;
+ public const COLUMN_TYPE = 2;
+ public const VALUE_TYPE_NUM = 3;
+ public const VALUE_TYPE_STR = 4;
- /**
- * Initiate the instance
- * @param array|string|int|float $value
- */
- public function __construct($value)
- {
- $this->value = $value;
- $this->raw = $value;
- }
+ private ConnectInterface|HandlerInterface $connection;
+ private float|int|array|string|null $value = null;
+ private float|int|array|string $raw;
+ //private array $set = [];
+ //private bool $hasBeenEncoded = false;
+
+ private int $type = 0;
+ private bool $prep = true;
+ private bool $sanitize = true;
+ private bool $enclose = true;
+ private bool $jsonEncode = false;
+ private bool $encode = false;
/**
* Initiate the instance
- * @param array|string|int|float $value
- * @return self
+ * @param ConnectInterface|HandlerInterface $connection
*/
- public static function value(array|string|int|float $value): self
+ public function __construct(ConnectInterface|HandlerInterface $connection)
{
- $inst = new self($value);
- return $inst;
+ $this->connection = $connection;
}
/**
- * Process string after your choises
+ * Process string after your choices
* @return string
*/
public function __toString(): string
@@ -47,88 +53,177 @@ public function __toString(): string
}
/**
- * Will escape and encode values the right way buy the default
- * If prepped then quotes will be escaped and not encoded
- * If prepped is diabled then quotes will be encoded
- * @return string
+ * IMMUTABLE: Set value you that want to encode against.
+ * Will also REST all values to its defaults
+ * @param float|int|array|string $value
+ * @return self
*/
- public function getValue(): string
+ public function withValue(float|int|array|string $value): self
{
- if (!$this->hasBeenEncoded) {
-
- $this->hasBeenEncoded = true;
- $this->value = Encode::value($this->value)
- ->specialChar($this->encode, ($this->prep ? ENT_NOQUOTES : ENT_QUOTES))
- ->urlEncode(false)
- ->encode();
-
- if ($this->jsonEncode && is_array($this->value)) {
- // If prep is on then escape after json_encode,
- // otherwise json encode will possibly escape the escaped value
- $this->value = json_encode($this->value);
- }
-
- if($this->prep) {
- $this->value = Connect::getInstance()->prep($this->value);
- }
+ $inst = new self($this->connection);
+ $inst->value = $value;
+ $inst->raw = $value;
+ return $inst;
+ }
- if ($this->enclose) {
- $this->value = "'{$this->value}'";
- }
+ public function type(int $dataType): static
+ {
+ $inst = clone $this;
+ if($dataType < 0 || $dataType > self::VALUE_TYPE_STR) {
+ throw new InvalidArgumentException('The data type expects to be either "RAW_TYPE (0), VALUE_TYPE (1),
+ COLUMN_TYPE (2), VALUE_TYPE_NUM (3), VALUE_TYPE_STR (4)"!');
+ }
+ $inst->type = $dataType;
+ if($dataType === self::RAW_TYPE) {
+ $inst = $inst->prep(false)->enclose(false)->encode(false)->sanitize(false);
}
- return $this->value;
+
+ if($dataType === self::VALUE_TYPE_NUM) {
+ $inst = $inst->enclose(false);
+ $inst->value = (float)$inst->value;
+ }
+
+ // Will not "prep" column type by default, but instead it will "sanitize"
+ if($dataType === self::COLUMN_TYPE) {
+ $inst = $inst->prep(false)->sanitize(true);
+ }
+ return $inst;
}
/**
- * Get raw data from instance
- * @return string|array
+ * Check value type
+ * @param int $type
+ * @return int
*/
- public function getRaw(): string|array
+ public function isType(int $type): int
{
- return $this->raw;
+ return ($this->type === $type);
}
/**
- * Enable/disable MySQL prep
+ * IMMUTABLE: Enable/disable MySQL prep
* @param bool $prep
- * @return self
+ * @return static
*/
- public function prep(bool $prep): self
+ public function prep(bool $prep): static
{
- $this->prep = $prep;
- return $this;
+ $inst = clone $this;
+ $inst->prep = $prep;
+ return $inst;
}
/**
- * Enable/disable string enclose
+ * Sanitize column types
+ * @param bool $sanitize
+ * @return $this
+ */
+ public function sanitize(bool $sanitize): static
+ {
+ $inst = clone $this;
+ $inst->sanitize = $sanitize;
+ return $inst;
+ }
+
+ /**
+ * CHANGE name to RAW?
+ * IMMUTABLE: Enable/disable string enclose
* @param bool $enclose
- * @return self
+ * @return static
*/
- public function enclose(bool $enclose): self
+ public function enclose(bool $enclose): static
{
- $this->enclose = $enclose;
- return $this;
+ $inst = clone $this;
+ $inst->enclose = $enclose;
+ return $inst;
}
/**
- * If Request[key] is array then auto convert it to json to make it database ready
+ * IMMUTABLE: If Request[key] is array then auto convert it to json to make it database ready
* @param bool $jsonEncode
- * @return self
+ * @return static
*/
- public function jsonEncode(bool $jsonEncode): self
+ public function jsonEncode(bool $jsonEncode): static
{
- $this->jsonEncode = $jsonEncode;
- return $this;
+ $inst = clone $this;
+ $inst->jsonEncode = $jsonEncode;
+ return $inst;
}
/**
- * Enable/disable XSS protection
+ * CHANGE name to special char??
+ * IMMUTABLE: Enable/disable XSS protection
* @param bool $encode (default true)
- * @return self
+ * @return static
+ */
+ public function encode(bool $encode): static
+ {
+ $inst = clone $this;
+ $inst->encode = $encode;
+ return $inst;
+ }
+
+ /**
+ * CHANGE NAME TO GET??
+ * Can only be encoded once
+ * Will escape and encode values the right way buy the default
+ * If prepped then quotes will be escaped and not encoded
+ * If prepped is disabled then quotes will be encoded
+ * @return string
*/
- public function encode(bool $encode): self
+ public function getValue(): string
{
- $this->encode = $encode;
- return $this;
+ $inst = clone $this;
+ if(is_null($inst->value)) {
+ throw new BadMethodCallException("You need to set a value first with \"withValue\"");
+ }
+
+ $inst->value = Encode::value($inst->value)
+ ->specialChar($inst->encode, ($inst->prep ? ENT_NOQUOTES : ENT_QUOTES))
+ ->sanitizeIdentifiers($inst->sanitize)
+ ->urlEncode(false)
+ ->encode();
+
+ // Array values will automatically be json encoded
+ if ($inst->jsonEncode || is_array($inst->value)) {
+ // If prep is on then escape after json_encode,
+ // otherwise json encode will possibly escape the escaped value
+ $inst->value = json_encode($inst->value);
+ }
+
+ if($inst->prep) {
+ $inst->value = $inst->connection->prep($inst->value);
+ }
+
+ if ($inst->enclose) {
+ // Do not use backticks in PostgreSQL
+ if(!(($inst->connection instanceof PostgreSQLConnect) && $inst->type === self::COLUMN_TYPE)) {
+ $inst->value = ($inst->type === self::COLUMN_TYPE) ? $this->getValueToColumn() : "'$inst->value'";
+ }
+ }
+
+ return $inst->value;
+ }
+
+ /**
+ * Will convert a value to a column type
+ * @return string
+ */
+ protected function getValueToColumn(): string
+ {
+ $arr = [];
+ $exp = explode('.', $this->value);
+ foreach($exp as $value) {
+ $arr[] = "`$value`";
+ }
+ return implode('.', $arr);
+ }
+
+ /**
+ * Get raw data from instance
+ * @return string|array
+ */
+ public function getRaw(): string|array
+ {
+ return $this->raw;
}
}
diff --git a/Utility/Build.php b/Utility/Build.php
deleted file mode 100755
index 6a39a1a..0000000
--- a/Utility/Build.php
+++ /dev/null
@@ -1,309 +0,0 @@
-", ">=", "<", "<>", "!=", "<=", "<=>"]; // Comparison operators
- protected const JOIN_TYPES = ["INNER", "LEFT", "RIGHT", "CROSS"]; // Join types
- protected const VIEW_PREFIX_NAME = "view"; // View prefix
-
- private $select;
-
- protected $table;
- protected $join;
- protected $joinedTables;
- protected $limit;
- protected $offset;
-
- protected $fkData;
- protected $mig;
-
- protected $attr;
-
-
-
- public function __construct(object|array|null $obj = null)
- {
-
- $this->attr = new \stdClass();
- if (!is_null($obj)) foreach($obj as $key => $value) {
- $this->attr->{$key} = $value;
- }
- }
-
-
- /**
- * Will build where string
- * @param string $prefix
- * @param array $where
- * @return string
- */
- public function where(string $prefix, ?array $where): string
- {
- $out = "";
- if (!is_null($where)) {
- $out = " {$prefix}";
- $index = 0;
- foreach ($where as $array) {
- $firstAnd = key($array);
- $out .= (($index > 0) ? " {$firstAnd}" : "") . " (";
- $out .= $this->whereArrToStr($array);
- $out .= ")";
- $index++;
- }
- }
- return $out;
- }
-
-
- /**
- * Build joins
- * @return string
- */
- public function join(
- string|array|MigrateInterface $table,
- string|array $where = null,
- array $sprint = array(),
- string $type = "INNER"
- ): string
- {
- if ($table instanceof MigrateInterface) {
- $this->join = array_merge($this->join, $this->buildJoinFromMig($table, $type));
- } else {
- $this->buildJoinFromArgs($table, $where, $sprint, $type);
- }
- return (is_array($this->join)) ? " " . implode(" ", $this->join) : "";
- }
-
- /**
- * Build limit
- * @return string
- */
- public function limit(): string
- {
- if (is_null($this->attr->limit) && !is_null($this->attr->offset)) {
- $this->attr->limit = 1;
- }
- $offset = (!is_null($this->attr->offset)) ? ",{$this->attr->offset}" : "";
- return (!is_null($this->attr->limit)) ? " LIMIT {$this->attr->limit}{$offset}" : "";
- }
-
-
- /**
- * Build Where data
- * @param array $array
- * @return string
- */
- final protected function whereArrToStr(array $array): string
- {
- $out = "";
- $count = 0;
- foreach ($array as $key => $arr) {
- foreach ($arr as $operator => $a) {
- if (is_array($a)) {
- foreach ($a as $col => $b) {
- foreach ($b as $val) {
- if ($count > 0) {
- $out .= "{$key} ";
- }
- $out .= "{$col} {$operator} {$val} ";
- $count++;
- }
- }
- } else {
- $out .= "{$key} {$a} ";
- $count++;
- }
- }
- }
-
- return $out;
- }
-
- /**
- * Build join data from Migrate data
- * @param MigrateInterface $mig
- * @param string $type Join type (INNER, LEFT, ...)
- * @return array
- */
- final protected function buildJoinFromMig(MigrateInterface $mig, string $type): array
- {
- $joinArr = array();
- $prefix = Connect::getInstance()->getHandler()->getPrefix();
- $main = $this->getMainFKData();
- $data = $mig->getData();
- $this->mig->mergeData($data);
- $migTable = $mig->getTable();
-
- foreach ($data as $col => $row) {
- if (isset($row['fk'])) {
- foreach ($row['fk'] as $a) {
- if ($a['table'] === (string)$this->attr->table) {
- $joinArr[] = "{$type} JOIN " . $prefix . $migTable . " " . $migTable .
- " ON (" . $migTable . ".{$col} = {$a['table']}.{$a['column']})";
- }
- }
- } else {
- foreach ($main as $c => $a) {
- foreach ($a as $t => $d) {
- if (in_array($col, $d)) {
- $joinArr[] = "{$type} JOIN " . $prefix . $migTable . " " . $migTable .
- " ON ({$t}.{$col} = {$this->attr->alias}.{$c})";
- }
- }
- }
- }
-
- $this->joinedTables[$migTable] = $prefix . $migTable;
- }
- return $joinArr;
- }
-
-
- protected function buildJoinFromArgs(
- string|array $table,
- string|array $where,
- array $sprint = array(),
- string $type = "INNER"
- ): void
- {
- if (is_null($where)) {
- throw new \InvalidArgumentException("You need to specify the argumnet 2 (where) value!", 1);
- }
-
- $prefix = Connect::getInstance()->getHandler()->getPrefix();
- $arr = $this->sperateAlias($table);
- $table = (string)$this->prep($arr['table'], false);
- $alias = (!is_null($arr['alias'])) ? " {$arr['alias']}" : " {$table}";
-
- if (is_array($where)) {
- $data = array();
- foreach ($where as $key => $val) {
- if (is_array($val)) {
- foreach ($val as $k => $v) {
- $this->setWhereData($k, $v, $data);
- }
- } else {
- $this->setWhereData($key, $val, $data);
- }
- }
- $out = $this->buildWhere("", $data);
- } else {
- $out = $this->sprint($where, $sprint);
- }
- $type = $this->joinTypes(strtoupper($type)); // Whitelist
- $this->join[] = "{$type} JOIN {$prefix}{$table}{$alias} ON " . $out;
- $this->joinedTables[$table] = "{$prefix}{$table}";
- }
-
- /**
- * Get the Main FK data protocol
- * @return array
- */
- final protected function getMainFKData(): array
- {
- if (is_null($this->fkData)) {
- $this->fkData = array();
- foreach ($this->mig->getMig()->getData() as $col => $row) {
- if (isset($row['fk'])) {
- foreach ($row['fk'] as $a) {
- $this->fkData[$col][$a['table']][] = $a['column'];
- }
- }
- }
- }
- return $this->fkData;
- }
-
-
- /**
- * Sperate Alias
- * @param string|array $data
- * @return array
- */
- final protected function sperateAlias(string|array|DBInterface $data): array
- {
- $alias = null;
- $table = $data;
- if (is_array($data)) {
- if (count($data) !== 2) {
- throw new DBQueryException("If you specify Table as array then it should look " .
- "like this [TABLE_NAME, ALIAS]", 1);
- }
- $alias = array_pop($data);
- $table = reset($data);
- }
- return ["alias" => $alias, "table" => $table];
- }
-
- /**
- * Mysql Prep/protect string
- * @param mixed $val
- * @return AttrInterface
- */
- final protected function prep(mixed $val, bool $enclose = true): AttrInterface
- {
- if ($val instanceof AttrInterface) {
- return $val;
- }
- $val = $this->getAttr($val);
- $val->enclose($enclose);
- return $val;
- }
-
- /**
- * Mysql Prep/protect array items
- * @param array $arr
- * @param bool $enclose
- * @return array
- */
- final protected function prepArr(array $arr, bool $enclose = true): array
- {
- $new = array();
- foreach ($arr as $pKey => $pVal) {
- $key = (string)$this->prep($pKey, false);
- $new[$key] = (string)$this->prep($pVal, $enclose);
- }
- return $new;
- }
-
- /**
- * Get new Attr instance
- * @param array|string|int|float $value
- * @return AttrInterface
- */
- protected function getAttr(array|string|int|float $value): AttrInterface
- {
- return new Attr($value);
- }
-
- /**
- * Use vsprintf to mysql prep/protect input in string. Prep string values needs to be eclosed manually
- * @param string $str SQL string example: (id = %d AND permalink = '%s')
- * @param array $arr Mysql prep values
- * @return string
- */
- final protected function sprint(string $str, array $arr = array()): string
- {
- return vsprintf($str, $this->prepArr($arr, false));
- }
-
- /**
- * Whitelist mysql join types
- * @param string $val
- * @return string
- */
- protected function joinTypes(string $val): string
- {
- $val = trim($val);
- if (in_array($val, $this::JOIN_TYPES)) {
- return $val;
- }
- return "INNER";
- }
-}
diff --git a/Utility/Helpers.php b/Utility/Helpers.php
new file mode 100644
index 0000000..90a5ae3
--- /dev/null
+++ b/Utility/Helpers.php
@@ -0,0 +1,241 @@
+", ">=", "<", "<>", "!=", "<=", "<=>"]; // Comparison operators
+ protected const JOIN_TYPES = ["INNER", "LEFT", "RIGHT", "CROSS"]; // Join types
+
+
+ /**
+ * Whitelist comparison operators
+ * @param string $val
+ * @return string
+ */
+ public static function operator(string $val): string
+ {
+ $val = trim($val);
+ if (in_array($val, static::OPERATORS)) {
+ return $val;
+ }
+ return "=";
+ }
+
+ /**
+ * Whitelist mysql sort directions
+ * @param string $val
+ * @return string
+ */
+ public static function orderSort(string $val): string
+ {
+ $val = strtoupper($val);
+ if ($val === "ASC" || $val === "DESC") {
+ return $val;
+ }
+ return "ASC";
+ }
+
+ /**
+ * Whitelist mysql join types
+ * @param string $val
+ * @return string
+ */
+ public static function joinTypes(string $val): string
+ {
+ $val = trim($val);
+ if (in_array($val, static::JOIN_TYPES)) {
+ return $val;
+ }
+ return "INNER";
+ }
+
+ /**
+ * Prepare order by
+ * @param array $arr
+ * @return array
+ */
+ public static function getOrderBy(array $arr): array
+ {
+ $new = [];
+ foreach($arr as $row) {
+ $new[] = "{$row['column']} {$row['sort']}";
+ }
+ return $new;
+ }
+
+
+ public static function buildJoinData(DBInterface $inst, string|array $where): array
+ {
+ $data = [];
+ if (is_array($where)) {
+ foreach ($where as $key => $val) {
+ if (is_array($val)) {
+ foreach ($val as $grpKey => $grpVal) {
+ if(!($grpVal instanceof AttrInterface)) {
+ $grpVal = "%s";
+ }
+ $inst->setWhereData($grpKey, $grpVal, $data);
+ }
+ } else {
+ if(!($val instanceof AttrInterface)) {
+ $val = "%s";
+ }
+ $inst->setWhereData($key, $val, $data);
+ }
+ }
+ }
+
+ return $data;
+ }
+
+ public function validateIdentifiers($column): bool
+ {
+ return (preg_match('/^[a-zA-Z0-9_]+$/', $column) !== false);
+ }
+
+ /**
+ * Mysql Prep/protect string
+ * @param mixed $val
+ * @param bool $enclose
+ * @return AttrInterface
+ */
+ public static function prep(mixed $val, bool $enclose = true): AttrInterface
+ {
+ if ($val instanceof AttrInterface) {
+ return $val;
+ }
+ $val = static::getAttr($val);
+ $val->enclose($enclose);
+ return $val;
+ }
+
+ /**
+ * Mysql Prep/protect array items
+ * @param array $arr
+ * @param bool $enclose
+ * @return array
+ */
+ public static function prepArr(array $arr, bool $enclose = true): array
+ {
+ $new = [];
+ foreach ($arr as $pKey => $pVal) {
+ $key = (string)static::prep($pKey, false);
+ $new[$key] = (string)static::prep($pVal, $enclose);
+ }
+ return $new;
+ }
+
+ /**
+ * MOVE TO DTO ARR
+ * Will extract camelcase to array
+ * @param string $value string value with possible camel cases
+ * @return array
+ */
+ public static function extractCamelCase(string $value): array
+ {
+ return preg_split('#([A-Z][^A-Z]*)#', $value, 0, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
+ }
+
+ /**
+ * Use to loop camel case method columns
+ * @param array $camelCaseArr
+ * @param array $valArr
+ * @param callable $call
+ * @return void
+ */
+ public static function camelLoop(array $camelCaseArr, array $valArr, callable $call): void
+ {
+ foreach ($camelCaseArr as $k => $col) {
+ $col = lcfirst($col);
+ $value = ($valArr[$k] ?? null);
+ $call($col, $value);
+ }
+ }
+
+ /**
+ * Get new Attr instance
+ * @param array|string|int|float $value
+ * @return AttrInterface
+ */
+ public static function getAttr(array|string|int|float $value): AttrInterface
+ {
+ return new Attr($value);
+ }
+
+ /**
+ * Access Query Attr class
+ * @param array|string|int|float $value
+ * @param array|null $args
+ * @return AttrInterface
+ */
+ public static function withAttr(array|string|int|float $value, ?array $args = null): AttrInterface
+ {
+ $inst = static::getAttr($value);
+ if (!is_null($args)) {
+ foreach ($args as $method => $arg) {
+ if (!method_exists($inst, $method)) {
+ throw new BadMethodCallException("The Query Attr method \"" .htmlspecialchars($method, ENT_QUOTES). "\" does not exists!", 1);
+ }
+ $inst = call_user_func_array([$inst, $method], (!is_array($arg) ? [$arg] : $arg));
+ }
+ }
+ return $inst;
+ }
+
+ /**
+ * Separate Alias
+ * @param string|array $data
+ * @return array
+ * @throws InvalidArgumentException
+ */
+ public static function separateAlias(string|array $data): array
+ {
+ $alias = null;
+ $table = $data;
+ if (is_array($data)) {
+ if (count($data) !== 2) {
+ throw new InvalidArgumentException("If you specify Table as array then it should look " .
+ "like this [TABLE_NAME, ALIAS]", 1);
+ }
+ $alias = array_pop($data);
+ $table = reset($data);
+ }
+ return ["alias" => $alias, "table" => $table];
+ }
+
+
+ /**
+ * Will add a alias to a MySQL table
+ * @param string $table
+ * @param string|null $alias
+ * @return string
+ */
+ public static function addAlias(string|AttrInterface $table, null|string|AttrInterface $alias = null, string $command = ""): string
+ {
+ if(!is_null($alias)) {
+ $table .= ($command ? " {$command} " : " ") . $alias;
+ }
+ return $table;
+ }
+
+ /**
+ * Will add a alias to a MySQL table
+ * @param string $table
+ * @param string|null $alias
+ * @return string
+ */
+ public static function toAlias(string|AttrInterface $table, null|string|AttrInterface $alias = null): string
+ {
+ if(!is_null($alias)) {
+ $table .= " AS " . $alias;
+ }
+ return $table;
+ }
+
+}
diff --git a/Utility/WhitelistMigration.php b/Utility/WhitelistMigration.php
index a8ee6fe..4385016 100755
--- a/Utility/WhitelistMigration.php
+++ b/Utility/WhitelistMigration.php
@@ -1,12 +1,11 @@
data[$key])) {
- $this->message = "The column ({$key}) do not exists in database table.";
+ $this->message = "The column ($key) do not exists in database table.";
return false;
}
@@ -130,10 +131,10 @@ public function where(string $key, AttrInterface $value): bool
$length = (int)($this->data[$key]['length'] ?? 0);
if ((in_array($type, self::TYPE_NUMERIC) && !is_numeric($value)) || !is_string($value)) {
- $this->message = "The database column ({$key}) value type is not supported.";
+ $this->message = "The database column ($key) value type is not supported.";
return false;
} elseif ($length < strlen($value)) {
- $this->message = "The database column ({$key}) value length is longer than ({$length}).";
+ $this->message = "The database column ($key) value length is longer than ($length).";
return false;
}
return true;
diff --git a/composer.json b/composer.json
index 9df6b57..db2f46a 100644
--- a/composer.json
+++ b/composer.json
@@ -23,7 +23,7 @@
],
"require": {
"php": ">=8.0",
- "maplephp/dto": "^1.0"
+ "maplephp/dto": "^2.0"
},
"require-dev": {
"maplephp/unitary": "^1.0"
diff --git a/tests/database.sqlite b/tests/database.sqlite
index aa5c121..959534d 100644
Binary files a/tests/database.sqlite and b/tests/database.sqlite differ
diff --git a/tests/unitary-database.php b/tests/unitary-database.php
new file mode 100755
index 0000000..33a8224
--- /dev/null
+++ b/tests/unitary-database.php
@@ -0,0 +1,130 @@
+add("Unitary test 3333", function () use ($unit) {
+
+ //$handler = new MySQLHandler(getenv("DATABASE_HOST"), getenv("DATABASE_USERNAME"), getenv("DATABASE_PASSWORD"), "test");
+ //$handler = new PostgreSQLHandler("127.0.0.1", "postgres", "", "postgres");
+ $handler = new SQLiteHandler(__DIR__ . "/database.sqlite");
+ $handler->setPrefix("maple_");
+ $db = new DBTest($handler);
+
+
+
+
+ //$p1 = $db->table("test")->where("parent", 0);
+
+
+ /*
+ for($i = 0; $i < 80000; $i++) {
+ //$db->getConnection()->query("SELECT * FROM maple_test WHERE parent=0");
+ //$p1->query("SELECT * FROM maple_test WHERE parent=0")->execute();
+ //$p1->query($p1->sql())->execute();
+ //$p1->execute();
+ }
+ */
+
+
+
+
+ /*
+ $value = '0';
+ $stmt = $db->getConnection()->prepare("SELECT * FROM maple_test WHERE parent=?");
+ for($i = 0; $i < 80000; $i++) {
+ $stmt->bind_param('s', $value);
+ $value = '1';
+ $stmt->execute();
+ }
+ $result = $stmt->get_result();
+ $stmt->close();
+ */
+
+ $startTime = microtime(true);
+ $startMemory = memory_get_usage();
+ $p1 = $db->table("test")->where("parent", 1);
+ $unit->performance(function() use ($db) {
+ $p1 = $db->table("test")->where("parent", 1);
+ $prepare = new Prepare($p1);
+ for($i = 0; $i < 40000; $i++) {
+ $prepare->bind($p1->where("parent", 1));
+ $prepare->execute();
+ }
+ });
+
+
+ $p1 = $db->table("test")->where("parent", 1);
+ $unit->performance(function() use ($db) {
+ $p1 = $db->table("test")->where("parent", 1);
+ $prepare = new Prepare($p1);
+ for($i = 0; $i < 1000; $i++) {
+ $prepare->bind($p1->where("parent", 1));
+ $prepare->execute();
+ }
+ });
+
+ $p1 = $db->table("test")->where("parent", 1);
+ $unit->performance(function() use ($db) {
+ $p1 = $db->table("test")->where("parent", 1);
+ $prepare = new Prepare($p1);
+ for($i = 0; $i < 10000; $i++) {
+ $prepare->bind($p1->where("parent", 1));
+ $prepare->execute();
+ }
+ });
+
+
+
+ /*
+ * $prepare = new Prepare($p1);
+ for($i = 0; $i < 10000; $i++) {
+ $prepare->bind($p1->where("parent", 1));
+ $prepare->execute();
+ }
+ */
+
+
+
+ /*
+
+
+
+ for($i = 0; $i < 50000; $i++) {
+ $p1->execute();
+ }
+ */
+
+
+
+ die;
+
+ /*
+ Execution time: 28.407850027084 seconds
+Memory used: 2291.7578125 KB
+Peak memory used: 5136.6484375 KB
+ */
+
+ $this->add("Lorem ipsum dolor", [
+ "isInt" => [],
+ "length" => [1,200]
+
+ ])->add(92928, [
+ "isInt" => []
+
+ ])->add("Lorem", [
+ "isString" => [],
+ "length" => function () {
+ return $this->length(1, 50);
+ }
+
+ ], "The length is not correct!");
+
+});
+
diff --git a/tests/unitary-db.php b/tests/unitary-db.php
index c42546c..65f0145 100755
--- a/tests/unitary-db.php
+++ b/tests/unitary-db.php
@@ -2,104 +2,118 @@
use database\migrations\Test;
use database\migrations\TestCategory;
+use MaplePHP\Query\DBTest;
+use MaplePHP\Query\Handlers\MySQLHandler;
use MaplePHP\Query\Handlers\PostgreSQLHandler;
use MaplePHP\Query\Handlers\SQLiteHandler;
+use MaplePHP\Query\Prepare;
+use MaplePHP\Query\Utility\Attr;
use MaplePHP\Unitary\Unit;
use MaplePHP\Query\Connect;
-use MaplePHP\Query\DB;
+
// Only validate if there is a connection open!
-if (Connect::hasInstance() && Connect::getInstance()->hasConnection()) {
+//if (Connect::hasInstance() && Connect::getInstance()->hasConnection()) {
$unit = new Unit();
+ $handler = new MySQLHandler(getenv("DATABASE_HOST"), getenv("DATABASE_USERNAME"), getenv("DATABASE_PASSWORD"), "test");
+ $handler->setPrefix("maple_");
+ $db = new DBTest($handler);
- // Add a title to your tests (not required)
- $unit->addTitle("Testing MaplePHP Query library!");
- $unit->add("MySql Query builder", function ($inst) {
-
- $db = Connect::getInstance();
- $select = $db::select("id,a.name,b.name AS cat", ["test", "a"])->whereParent(0)->where("status", 0, ">")->limit(6);
- $select->join(["test_category", "b"], "tid = id");
-
- // 3 queries
- $obj = $select->get();
- $arr = $select->fetch();
- $pluck = DB::table("test")->pluck("name")->get();
-
- $inst->add($obj, [
- "isObject" => [],
- "missingColumn" => function () use ($obj) {
- return (isset($obj->name) && isset($obj->cat));
- }
- ], "Data is missing");
-
- $inst->add($arr, [
- "isArray" => [],
- "noRows" => function () use ($arr) {
- return (count($arr) > 0);
- }
- ], "Fetch feed empty");
-
- $inst->add($pluck, [
+
+
+
+
+ $unit->case("OK", function () use ($unit) {
+
+ $this->add("Test", [
"isString" => [],
- "length" => [1]
- ], "Pluck is expected to return string");
-
- $select = $db::select("id,test.name,test_category.name AS cat", new Test)->whereParent(0)->where("status", 0, ">")->limit(6);
- $select->join(new TestCategory);
- $obj = $select->obj();
-
- $inst->add($obj, [
- "isObject" => [],
- "missingColumn" => function () use ($obj) {
- return (isset($obj->name) && isset($obj->cat));
- }
- ], "Data is missing");
- });
+ "length" => [1, 200]
+ ]);
- /**
- * This will test multiple databases AND
- * validate sqLite database
- */
- $unit->add("sqLite Query builder", function ($inst) {
-
- $sqLiteHandler = new SQLiteHandler(__DIR__ . "/database.sqlite");
- $sqLiteHandler->setPrefix("mp_");
- $connect = Connect::setHandler($sqLiteHandler, "lite");
- $connect->execute();
-
- // Access sqLite connection
- $select = Connect::getInstance("lite")::select("id,name,content", "test")->whereStatus(1)->limit(3);
- $result = $select->fetch();
- $inst->add($result, [
- "isArray" => [],
- "rows" => function () use ($result) {
- return (count($result) === 3);
- }
- ], "Fetch should equal to 3");
- });
+ $this->add("Testwqd", [
+ "length" => [1, 200]
+ ], "Test data type");
- /**
- * This will test multiple databases AND
- * validate sqLite database
- */
- $unit->add("sqLite Query builder", function ($inst) {
-
- $sqLiteHandler = new PostgreSQLHandler("127.0.0.1", "postgres", "", "maplephp");
- $sqLiteHandler->setPrefix("maple_");
- $connect = Connect::setHandler($sqLiteHandler, "psg");
- $connect->execute();
-
- // Access sqLite connection
- $select = Connect::getInstance("psg")::select("id,name", ["test", "a"])->limit(2);
- $result = $select->fetch();
- $inst->add($result, [
- "isArray" => [],
- "rows" => function () use ($result) {
- return (count($result) === 2);
- }
- ], "Fetch should equal to 2");
});
- $unit->execute();
-}
+//}
+
+
+/*
+
+ $instances = [null, "postgresql", "sqlite"];
+
+ $sqLiteHandler = new PostgreSQLHandler("127.0.0.1", "postgres", "", "postgres");
+ $sqLiteHandler->setPrefix("maple_");
+ $connect = Connect::setHandler($sqLiteHandler, "postgresql");
+ $connect->execute();
+
+ $sqLiteHandler = new SQLiteHandler(__DIR__ . "/database.sqlite");
+ $sqLiteHandler->setPrefix("maple_");
+ $connect = Connect::setHandler($sqLiteHandler, "sqlite");
+ $connect->execute();
+
+
+ // Add a title to your tests (not required)
+ $unit->addTitle("Testing MaplePHP Query library!");
+ foreach($instances as $key) {
+ $message = "Error in " . (is_null($key) ? "mysql" : $key);
+ $unit->add($message, function ($inst) use ($unit, $key, $instances) {
+
+ // Select handler
+ $db = Connect::getInstance($key);
+
+ $inst->add($db->hasConnection(), [
+ "equal" => [true],
+ ], "Missing connection");
+
+ $select = $db::select("test.id", "test")
+ ->whereBind(function ($inst) {
+ $inst->not()
+ ->where("status", 0)
+ ->or()
+ ->where("status", 0, ">");
+ })
+ ->whereParent(0)
+ ->having("id", 0, ">")
+ ->whereRaw("id > 0")
+ ->havingRaw("COUNT(id) > 0")
+ ->group("id")
+ ->distinct("id")
+ ->limit(2);
+ $select->join(["test_category", "b"], "tid = id");
+ $arr = $select->fetch();
+
+
+ //$unit->command()->message($select->sql());
+ $inst->add(count($arr), [
+ "equal" => [2],
+ ], "Data is missing");
+
+
+ // Test union
+ $union = $db::select("id,name", "test");
+ $unit->command()->message(Connect::$current);
+
+ $select = $db::select("cat_id AS id,name", "test_category");
+
+
+
+ $insert = $db::insert("test")->set([
+ "name" => "Test row",
+ "content" => "Delete this row",
+ "status" => 1,
+ "parent" => 0,
+ "create_date" => date("Y-m-d H:i:s", time()),
+ ]);
+
+
+
+
+ //print_r($db->connection());
+ //$insert->execute();
+
+ });
+ }
+ */
\ No newline at end of file