connection = NULL; $this->lastError = 'Unknown Error'; $this->connectionData = $connectionData; $this->lastInsertId = 0; } public function isConnected() { return !is_null($this->connection); } public function getLastError() { return trim($this->lastError); } public function createTable($tableName) { return new CreateTable($this, $tableName); } public function insert($tableName, $columns=array()) { return new Insert($this, $tableName, $columns); } public function select(...$columNames) { return new Select($this, $columNames); } public function truncate($table) { return new Truncate($this, $table); } public function delete($table) { return new Delete($this, $table); } public function update($table) { return new Update($this, $table); } public function drop(string $table) { return new Drop($this, $table); } // #################### // ### ABSTRACT METHODS // #################### // Misc public abstract function checkRequirements(); public abstract function getDriverName(); // Connection Managment public abstract function connect(); public abstract function disconnect(); // Querybuilder protected function buildQuery(Query $query, array &$params) { if ($query instanceof Select) { $select = $query; $columns = $this->columnName($select->getColumns()); $tables = $select->getTables(); if (!$tables) { return $this->execute("SELECT $columns", $params, true); } $tables = $this->tableName($tables); $where = $this->getWhereClause($select->getConditions(), $params); $joinStr = ""; $joins = $select->getJoins(); if (!empty($joins)) { foreach($joins as $join) { $type = $join->getType(); $joinTable = $this->tableName($join->getTable()); $columnA = $this->columnName($join->getColumnA()); $columnB = $this->columnName($join->getColumnB()); $tableAlias = ($join->getTableAlias() ? " " . $join->getTableAlias() : ""); $joinStr .= " $type JOIN $joinTable$tableAlias ON $columnA=$columnB"; } } $groupBy = ""; $groupColumns = $select->getGroupBy(); if (!empty($groupColumns)) { $groupBy = " GROUP BY " . $this->columnName($groupColumns); } $orderBy = ""; $orderColumns = $select->getOrderBy(); if (!empty($orderColumns)) { $orderBy = " ORDER BY " . $this->columnName($orderColumns); $orderBy .= ($select->isOrderedAscending() ? " ASC" : " DESC"); } $limit = ($select->getLimit() > 0 ? (" LIMIT " . $select->getLimit()) : ""); $offset = ($select->getOffset() > 0 ? (" OFFSET " . $select->getOffset()) : ""); return "SELECT $columns FROM $tables$joinStr$where$groupBy$orderBy$limit$offset"; } else { $this->lastError = "buildQuery() not implemented for type: " . get_class($query); return FALSE; } } public function executeCreateTable(CreateTable $createTable) { $tableName = $this->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 $insert) { $tableName = $this->tableName($insert->getTableName()); $columns = $insert->getColumns(); $rows = $insert->getRows(); if (empty($rows)) { $this->lastError = "No rows to insert given."; return false; } if (is_null($columns) || empty($columns)) { $columnStr = ""; } else { $columnStr = " (" . $this->columnName($columns) . ")"; } $parameters = array(); $values = array(); foreach($rows as $row) { $rowPlaceHolder = array(); foreach($row as $val) { $rowPlaceHolder[] = $this->addValue($val, $parameters); } $values[] = "(" . implode(",", $rowPlaceHolder) . ")"; } $values = implode(",", $values); $onDuplicateKey = $this->getOnDuplicateStrategy($insert->onDuplicateKey(), $parameters); if ($onDuplicateKey === FALSE) { return false; } $returningCol = $insert->getReturning(); $returning = $this->getReturning($returningCol); $query = "INSERT INTO $tableName$columnStr VALUES $values$onDuplicateKey$returning"; if($insert->dump) { var_dump($query); var_dump($parameters); } $res = $this->execute($query, $parameters, !empty($returning)); $success = ($res !== FALSE); if($success && $returningCol) { $this->fetchReturning($res, $returningCol); } return $success; } public function executeSelect(Select $select) { $params = array(); $query = $this->buildQuery($select, $params); if($select->dump) { var_dump($query); var_dump($params); } return $this->execute($query, $params, true); } public function executeDelete(Delete $delete) { $params = array(); $table = $this->tableName($delete->getTable()); $where = $this->getWhereClause($delete->getConditions(), $params); $query = "DELETE FROM $table$where"; if($delete->dump) { var_dump($query); } return $this->execute($query, $params); } public function executeTruncate(Truncate $truncate) { $query = "TRUNCATE " . $this->tableName($truncate->getTable()); if ($truncate->dump) { var_dump($query); } return $this->execute($query); } public function executeUpdate(Update $update) { $params = array(); $table = $this->tableName($update->getTable()); $valueStr = array(); foreach($update->getValues() as $key => $val) { $valueStr[] = $this->columnName($key) . "=" . $this->addValue($val, $params); } $valueStr = implode(",", $valueStr); $where = $this->getWhereClause($update->getConditions(), $params); $query = "UPDATE $table SET $valueStr$where"; if($update->dump) { var_dump($query); var_dump($params); } return $this->execute($query, $params); } public function executeDrop(Drop $drop) { $query = "DROP TABLE " . $this->tableName($drop->getTable()); if ($drop->dump) { var_dump($query); } return $this->execute($query); } protected function getWhereClause($conditions, &$params) { if (!$conditions) { return ""; } else { return " WHERE " . $this->buildCondition($conditions, $params); } } public function getConstraintDefinition(Constraint $constraint) { $columnName = $this->columnName($constraint->getColumnNames()); if ($constraint instanceof PrimaryKey) { return "PRIMARY KEY ($columnName)"; } else if ($constraint instanceof Unique) { return "UNIQUE ($columnName)"; } else if ($constraint instanceof ForeignKey) { $refTable = $this->tableName($constraint->getReferencedTable()); $refColumn = $this->columnName($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; } else { $this->lastError = "Unsupported constraint type: " . get_class($constraint); return false; } } protected function getReturning(?string $columns) { return ""; } protected abstract function getColumnDefinition(Column $column); protected abstract function fetchReturning($res, string $returningCol); protected abstract function getOnDuplicateStrategy(?Strategy $strategy, &$params); protected abstract function getValueDefinition($val); protected abstract function addValue($val, &$params); protected abstract function tableName($table); protected abstract function columnName($col); // Special Keywords and functions public function now() { return $this->currentTimestamp(); } public abstract function currentTimestamp(); public function count($col = NULL) { if (is_null($col)) { return new Keyword("COUNT(*) AS count"); } else if($col instanceof Keyword) { return new Keyword("COUNT(" . $col->getValue() . ") AS count"); } else { $countCol = strtolower(str_replace(".","_", $col)) . "_count"; $col = $this->columnName($col); return new Keyword("COUNT($col) AS $countCol"); } } public function sum($col) { $sumCol = strtolower(str_replace(".","_", $col)) . "_sum"; $col = $this->columnName($col); return new Keyword("SUM($col) AS $sumCol"); } public function distinct($col) { $col = $this->columnName($col); return new Keyword("DISTINCT($col)"); } // Statements protected abstract function execute($query, $values=NULL, $returnValues=false); protected function buildCondition($condition, &$params) { if ($condition instanceof CondOr) { $conditions = array(); foreach($condition->getConditions() as $cond) { $conditions[] = $this->buildCondition($cond, $params); } return "(" . implode(" OR ", $conditions) . ")"; } else if ($condition instanceof Compare) { $column = $this->columnName($condition->getColumn()); $value = $condition->getValue(); $operator = $condition->getOperator(); if ($value === null) { if ($operator === "=") { return "$column IS NULL"; } else if ($operator === "!=") { return "$column IS NOT NULL"; } } return $column . $operator . $this->addValue($value, $params); } else if ($condition instanceof CondBool) { return $this->columnName($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); } } else if($condition instanceof CondIn) { $expression = $condition->getExpression(); if (is_array($expression)) { $values = array(); foreach ($expression as $value) { $values[] = $this->addValue($value, $params); } $values = implode(",", $values); } else if($expression instanceof Select) { $values = $this->buildQuery($expression, $params); } else { $this->lastError = "Unsupported in-expression value: " . get_class($condition); return false; } return $this->columnName($condition->getColumn()) . " IN ($values)"; } else if($condition instanceof CondKeyword) { $left = $condition->getLeftExp(); $right = $condition->getRightExp(); $keyword = $condition->getKeyword(); $left = ($left instanceof Column) ? $this->columnName($left->getName()) : $this->addValue($left, $params); $right = ($right instanceof Column) ? $this->columnName($right->getName()) : $this->addValue($right, $params); return "$left $keyword $right "; } else if($condition instanceof CondNot) { $expression = $condition->getExpression(); if ($expression instanceof Condition) { $expression = $this->buildCondition($expression, $params); } else { $expression = $this->columnName($expression); } return "NOT $expression"; } else if($condition instanceof CondNull) { return $this->columnName($condition->getColumn()) . " IS NULL"; } else { $this->lastError = "Unsupported condition type: " . get_class($condition); return false; } } public function setLastError($str) { $this->lastError = $str; } public function getLastInsertId() { return $this->lastInsertId; } public function close() { $this->disconnect(); $this->connection = NULL; } public static function createConnection(ConnectionData $connectionData) { $type = $connectionData->getProperty("type"); if ($type === "mysql") { $sql = new MySQL($connectionData); } else if ($type === "postgres") { $sql = new PostgreSQL($connectionData); /*} else if ($type === "oracle") { // $sql = new OracleSQL($connectionData); */ } else { return "Unknown database type"; } if ($sql->checkRequirements()) { $sql->connect(); } return $sql; } public abstract function getStatus(); public function parseBool($val) : bool { return in_array($val, array(true, 1, '1', 't', 'true', 'TRUE'), true); } }