connection = new PDO("mysql:host=$hostname;dbname=$database", $user, $password); } catch (Throwable $e) { throw new DatabaseException( 'Couldn\'t connect to the database!', ServerStatus::INTERNAL_ERROR ); } } public function __destruct() { $this->connection = null; } public function Query(string $query, array $params = []): void { $this->cursor = $this->connection->prepare($query); if (!$this->cursor) { throw new DatabaseException( 'Initialization of database cursor failed', DatabaseException::CONNECTION_FAILED ); } foreach ($params as $key => $param) { if (is_bool($param)) { $param = (int)$param; } $this->cursor->bindValue(':' . $key, $param); } if (!$this->cursor->execute()) { throw new DatabaseException($this->cursor->errorInfo()[2], $this->cursor->errorInfo()[1]); } } public function getResult(): array { $result = []; while ($fetch = $this->cursor->fetchObject()) { $row = []; foreach (get_object_vars($fetch) as $key => $value) { $row[$key] = $value; } $result[] = $row; } return $result; } /** * Selects backend from a table. */ public function Select( string $tableName, array $fields = [], array $conditions = [], int $limit = 0, array $orderBy = [], bool $asc = true, int $offset = 0 ): array { if (!self::isValidTableName($tableName)) { []; } if (count($fields) === 0) { $fieldsExpression = '*'; } else { $fieldsExpression = implode(',', $fields); } $conditionsExpression = ''; $conditionPairs = []; foreach ($conditions as $field => $value) { $conditionPairs[] = sprintf('%s = :%s ', $field, $field); } if (count($conditions) > 0) { $conditionsExpression = 'WHERE '; $conditionsExpression .= implode(' AND ', $conditionPairs); } $orderStatement = ''; if (count($orderBy) > 0) { $orderStatement = 'ORDER BY ' . implode(',', $orderBy); if (!$asc) { $orderStatement .= ' DESC'; } } $limitStatement = ''; if ($limit > 0) { $limitStatement = 'LIMIT ' . $limit; } $offsetStatement = ''; if ($offset > 0) { $offsetStatement = 'OFFSET ' . $offset; } $query = sprintf( 'SELECT %s FROM %s %s %s %s %s', $fieldsExpression, $tableName, $conditionsExpression, $orderStatement, $limitStatement, $offsetStatement ); try { $this->Query($query, $conditions); } catch (Throwable $e) { throw new DatabaseException( $e->getMessage(), ServerStatus::INTERNAL_ERROR ); } return $this->getResult(); } /** * Deletes rows from a table. */ public function Delete(string $table, array $conditions): void { if (count($conditions) === 0) { $conditionsStatement = '1'; } else { $conditionPairs = []; foreach ($conditions as $field => $value) { $conditionPairs[] = sprintf('%s=:Condition%s', $field, $field); $conditions['Condition' . $field] = $value; unset($conditions[$field]); } $conditionsStatement = implode(' AND ', $conditionPairs); } $query = sprintf('DELETE FROM %s WHERE %s', $table, $conditionsStatement); $this->Query($query, $conditions); } public function Insert(string $table, array $fields): ?int { if (count($fields) === 0) { throw new DatabaseException('Row to insert is empty!'); } $fieldNames = implode(',', array_keys($fields)); $fieldPlaceholder = []; foreach ($fields as $name => $value) { $fieldPlaceholder[] = ':' . $name; } $query = sprintf( 'INSERT INTO %s (%s) VALUES (%s)', $table, $fieldNames, implode(',', $fieldPlaceholder) ); $this->Query($query, $fields); $lastInsertedId = $this->GetLastInsertedId(); if ((int)$lastInsertedId === 0) { return null; } return $lastInsertedId; } public function Update(string $table, array $fields, array $conditions): void { $conditionPairs = []; foreach ($conditions as $field => $value) { $conditionPairs[] = sprintf('%s=:Condition%s', $field, $field); $conditions['Condition' . $field] = $value; unset($conditions[$field]); } $conditionsStatement = implode(' AND ', $conditionPairs); $fieldPairs = []; foreach ($fields as $field => $value) { $fieldPairs[] = sprintf('%s=:%s', $field, $field); } $fieldsStatement = implode(',', $fieldPairs); $query = sprintf('UPDATE %s SET %s WHERE %s', $table, $fieldsStatement, $conditionsStatement); $this->Query($query, array_merge($fields, $conditions)); } public function Count(string $table, array $conditions = []): int { $result = $this->Select($table, ['count(*)'], $conditions); return (int)$result[0]['count(*)']; } public function hasTransaction(): bool { return $this->isTransactionOpen; } public function startTransaction(): void { $this->connection->beginTransaction(); $this->isTransactionOpen = true; } public function rollback(): void { $this->connection->rollBack(); $this->isTransactionOpen = false; } public function commit(): void { $this->connection->commit(); $this->isTransactionOpen = false; } public function GetLastInsertedId(): int { $this->Query('SELECT LAST_INSERT_ID() as ID'); return (int)$this->getResult()[0]['ID']; } /** * Does a check if the given table name contains forbidden chars. */ private static function isValidTableName(string $tableName): bool { foreach (str_split($tableName) as $char) { if (substr_count(self::CHARS_ALLOWED_IN_TABLE_NAMES, $char) === 0) { return false; } } return true; } }