connection)) { return true; } @$this->connection = mysqli_connect( $this->connectionData->getHost(), $this->connectionData->getLogin(), $this->connectionData->getPassword(), $this->connectionData->getProperty('database'), $this->connectionData->getPort() ); if (mysqli_connect_errno($this->connection)) { $this->lastError = "Failed to connect to MySQL: " . mysqli_connect_error(); $this->connection = NULL; return false; } mysqli_set_charset($this->connection, $this->connectionData->getProperty('encoding')); return true; } public function disconnect() { if(is_null($this->connection)) { return true; } mysqli_close($this->connection); $this->connection = NULL; } public function getLastError() { $lastError = parent::getLastError(); if (empty($lastError)) { $lastError = mysqli_error($this->connection); } return $lastError; } private function getPreparedParams($values) { $sqlParams = array(''); foreach($values as $value) { $paramType = Parameter::parseType($value); switch($paramType) { case Parameter::TYPE_BOOLEAN: $value = $value ? 1 : 0; case Parameter::TYPE_INT: $sqlParams[0] .= 'i'; break; case Parameter::TYPE_FLOAT: $sqlParams[0] .= 'd'; break; case Parameter::TYPE_DATE: $value = $value->format('Y-m-d'); $sqlParams[0] .= 's'; break; case Parameter::TYPE_TIME: $value = $value->format('H:i:s'); $sqlParams[0] .= 's'; break; case Parameter::TYPE_DATE_TIME: $value = $value->format('Y-m-d H:i:s'); $sqlParams[0] .= 's'; break; case Parameter::TYPE_EMAIL: default: $sqlParams[0] .= 's'; } $sqlParams[] = $value; } return $sqlParams; } protected function execute($query, $values = NULL, $returnValues = false) { $resultRows = array(); $this->lastError = ""; if (is_null($values) || empty($values)) { $res = mysqli_query($this->connection, $query); $success = $res !== FALSE; if ($success && $returnValues) { while($row = $res->fetch_assoc()) { $resultRows[] = $row; } $res->close(); } } else if($stmt = $this->connection->prepare($query)) { $success = false; $sqlParams = $this->getPreparedParams($values); $tmp = array(); foreach($sqlParams as $key => $value) $tmp[$key] = &$sqlParams[$key]; if(call_user_func_array(array($stmt, "bind_param"), $tmp)) { if($stmt->execute()) { if ($returnValues) { $res = $stmt->get_result(); if($res) { while($row = $res->fetch_assoc()) { $resultRows[] = $row; } $res->close(); $success = true; } else { $this->lastError = "PreparedStatement::get_result failed: $stmt->error ($stmt->errno)"; } } else { $success = true; } } else { $this->lastError = "PreparedStatement::execute failed: $stmt->error ($stmt->errno)"; } } else { $this->lastError = "PreparedStatement::prepare failed: $stmt->error ($stmt->errno)"; } $stmt->close(); } else { $success = false; } return ($success && $returnValues) ? $resultRows : $success; } public function executeCreateTable($createTable) { $tableName = $createTable->getTableName(); $ifNotExists = $createTable->ifNotExists() ? " IF NOT EXISTS": ""; $entries = array(); foreach($createTable->getColumns() as $column) { $entries[] = ($tmp = $this->getColumnDefinition($column)); if (is_null($tmp)) { return false; } } foreach($createTable->getConstraints() as $constraint) { $entries[] = ($tmp = $this->getConstraintDefinition($constraint)); if (is_null($tmp)) { return false; } } $entries = implode(",", $entries); $query = "CREATE TABLE$ifNotExists `$tableName` ($entries)"; return $this->execute($query); } public function executeInsert($insert) { $tableName = $insert->getTableName(); $columns = $insert->getColumns(); $rows = $insert->getRows(); $onDuplicateKey = $insert->onDuplicateKey() ?? ""; if (empty($rows)) { $this->lastError = "No rows to insert given."; return false; } if (is_null($columns) || empty($columns)) { $columns = ""; $numColumns = count($rows[0]); } else { $numColumns = count($columns); $columns = " (`" . implode("`, `", $columns) . "`)"; } $numRows = count($rows); $parameters = array(); $values = implode(",", array_fill(0, $numRows, "(" . implode(",", array_fill(0, $numColumns, "?")) . ")")); foreach($rows as $row) { $parameters = array_merge($parameters, $row); } if ($onDuplicateKey) { if ($onDuplicateKey instanceof UpdateStrategy) { $updateValues = array(); foreach($onDuplicateKey->getValues() as $key => $value) { if ($value instanceof Column) { $columnName = $value->getName(); $updateValues[] = "`$key`=`$columnName`"; } else { $updateValues[] = "`$key`=?"; $parameters[] = $value; } } $onDuplicateKey = " ON DUPLICATE KEY UPDATE " . implode(",", $updateValues); } else { $strategy = get_class($onDuplicateKey); $this->lastError = "ON DUPLICATE Strategy $strategy is not supported yet."; return false; } } $query = "INSERT INTO `$tableName`$columns VALUES$values$onDuplicateKey"; $success = $this->execute($query, $parameters); if($success) { $this->lastInsertId = mysqli_insert_id($this->connection); } return $success; } public function executeSelect($select) { $columns = implode(",", $select->getColumns()); $tables = $select->getTables(); $params = array(); if (is_null($tables) || empty($tables)) { return "SELECT $columns"; } else { $tables = implode(",", $tables); } $conditions = $select->getConditions(); if (!empty($conditions)) { $condition = " WHERE " . $this->buildCondition($conditions, $params); } else { $condition = ""; } $joinStr = ""; $joins = $select->getJoins(); if (!empty($joins)) { $joinStr = ""; foreach($joins as $join) { $type = $join->getType(); $joinTable = $join->getTable(); $columnA = $join->getColumnA(); $columnB = $join->getColumnB(); $joinStr .= " $type JOIN $joinTable ON $columnA=$columnB"; } } $orderBy = ""; $limit = ""; $offset = ""; $query = "SELECT $columns FROM $tables$joinStr$condition$orderBy$limit$offset"; return $this->execute($query, $params, true); } public function executeDelete($delete) { $table = $delete->getTable(); $conditions = $delete->getConditions(); if (!empty($conditions)) { $condition = " WHERE " . $this->buildCondition($conditions, $params); } else { $condition = ""; } $query = "DELETE FROM $table$condition"; return $this->execute($query); } public function executeTruncate($truncate) { return $this->execute("TRUNCATE " . $truncate->getTable()); } public function executeUpdate($update) { $params = array(); $table = $update->getTable(); $valueStr = array(); foreach($update->getValues() as $key => $val) { $valueStr[] = "$key=" . $this->addValue($val, $params); } $valueStr = implode(",", $valueStr); $conditions = $update->getConditions(); if (!empty($conditions)) { $condition = " WHERE " . $this->buildCondition($conditions, $params); } else { $condition = ""; } $query = "UPDATE $table SET $valueStr$condition"; return $this->execute($query, $params); } protected function buildCondition($condition, &$params) { if ($condition instanceof \Driver\SQL\Condition\CondOr) { $conditions = array(); foreach($condition->getConditions() as $cond) { $conditions[] = $this->buildCondition($cond, $params); } return "(" . implode(" OR ", $conditions) . ")"; } else if ($condition instanceof \Driver\SQL\Condition\Compare) { $column = $condition->getColumn(); $value = $condition->getValue(); $operator = $condition->getOperator(); return $column . $operator . $this->addValue($value, $params); } else if ($condition instanceof \Driver\SQL\Condition\CondBool) { return $condition->getValue(); } else if (is_array($condition)) { if (count($condition) == 1) { return $this->buildCondition($condition[0], $params); } else { $conditions = array(); foreach($condition as $cond) { $conditions[] = $this->buildCondition($cond, $params); } return implode(" AND ", $conditions); } } } public function getColumnDefinition($column) { $columnName = $column->getName(); if ($column instanceof StringColumn) { $maxSize = $column->getMaxSize(); if ($maxSize) { $type = "VARCHAR($maxSize)"; } else { $type = "TEXT"; } } else if($column instanceof SerialColumn) { $type = "INTEGER AUTO_INCREMENT"; } else if($column instanceof IntColumn) { $type = "INTEGER"; } else if($column instanceof DateTimeColumn) { $type = "DATETIME"; } else if($column instanceof EnumColumn) { $values = array(); foreach($column->getValues() as $value) { $values[] = $this->getValueDefinition($value); } $values = implode(",", $values); $type = "ENUM($values)"; } else if($column instanceof BoolColumn) { $type = "BOOLEAN"; } else if($column instanceof JsonColumn) { $type = "JSON"; } else { $this->lastError = "Unsupported Column Type: " . get_class($column); return NULL; } $notNull = $column->notNull() ? " NOT NULL" : ""; $defaultValue = (!is_null($column->getDefaultValue()) || !$column->notNull()) ? " DEFAULT " . $this->getValueDefinition($column->getDefaultValue()) : ""; return "`$columnName` $type$notNull$defaultValue"; } public function getConstraintDefinition($constraint) { $columnName = $constraint->getColumnName(); if ($constraint instanceof PrimaryKey) { if (is_array($columnName)) $columnName = implode('`,`', $columnName); return "PRIMARY KEY (`$columnName`)"; } else if ($constraint instanceof Unique) { if (is_array($columnName)) $columnName = implode('`,`', $columnName); return "UNIQUE (`$columnName`)"; } else if ($constraint instanceof ForeignKey) { $refTable = $constraint->getReferencedTable(); $refColumn = $constraint->getReferencedColumn(); $strategy = $constraint->onDelete(); $code = "FOREIGN KEY (`$columnName`) REFERENCES `$refTable` (`$refColumn`)"; if ($strategy instanceof SetDefaultStrategy) { $code .= " ON DELETE SET DEFAULT"; } else if($strategy instanceof SetNullStrategy) { $code .= " ON DELETE SET NULL"; } else if($strategy instanceof CascadeStrategy) { $code .= " ON DELETE CASCADE"; } return $code; } } // TODO: check this please.. // TODO: Constants?? public function getValueDefinition($value) { if (is_numeric($value) || is_bool($value)) { return $value; } else if(is_null($value)) { return "NULL"; } else if($value instanceof Keyword) { return $value->getValue(); } else { $str = addslashes($value); return "'$str'"; } } public function currentTimestamp() { return "NOW()"; } };