initHandler(); } /** * Opens a new connection to the database. * * @param string $host The host name * @param string $user The user name to authenticate * @param string $pass The user password * @param string $name The default schema name * * @return boolean %TRUE on success, %FALSE otherwise */ function open($host, $user, $pass, $name, $port = NULL) { $conn = $this->initHandler(); $conn->options(MYSQLI_OPT_LOCAL_INFILE, TRUE); $conn->real_connect($host, $user, $pass, $name, $port); if (mysqli_connect_errno()) { sleep(3); throw new Exception(mysqli_connect_errno(), mysqli_connect_error()); return FALSE; } $this->isOpen = TRUE; $this->query('SET CHARACTER SET utf8'); return TRUE; } /** * Closes the current connection, if it's closed does nothing. */ function close() { if ($this->isOpen) { $this->conn->close(); $this->conn = NULL; } $this->isOpen = FALSE; } /** * Initializes the internal database connection handler. * * @return Objetct The connection handler */ function initHandler() { if (!$this->conn) $this->conn = new \mysqli(); return $this->conn; } /** * Returns the internal database connection handler. This function shoud be * used to set options for specific databases that could not be set using * the Connection class methods. * * @return Objetct The connection handler */ function getHandler() { return $this->conn; } /** * Changes the default schema for the current connection. * * @param string $schema The schema name * @return boolean %TRUE if success, %FALSE otherwise */ function selectDb($dbName) { return $this->conn->select_db($dbName); } /** * Executes the query and gets the result. * * @param string $query The SQL query * @param mixed[] $params The query parameters * * @return mixed The value or %NULL if error */ function query($query, $params = NULL) { $result = $this->conn->query($this->renderDebug($query, $params)); if (!$result) $this->checkError(); else while ($this->moreResults()) $this->nextResult(); return $result; } /** * Checks whether the connection is open. * * @return boolean %TRUE if connection is open, %FALSE otherwise */ function isOpen() { return $this->isOpen; } /** * Gets the first row value of the first column from a result. * * @param resource $result The database result * * @return mixed[] An associative array with the first row, %NULL if error */ function getRowFromResult($result) { if ($result) { $row = $result->fetch_assoc(); $result->free(); return $row; } return NULL; } /** * Gets the first row value of the first column from a result. * * @param resource $result The database result * * @return object An object with the first row, %NULL if error */ function getObjectFromResult($result) { if ($result) { $row = $result->fetch_object(); $result->free(); return $row; } return NULL; } /** * Gets the first row value of the first column from a result. * * @param resource $result The database result * * @return mixed The value or %NULL if error */ function getValueFromResult($result) { $value = NULL; if ($result) { $row = $result->fetch_row(); if ($row && count($row) > 0) $value = $row[0]; $result->free(); } return $value; } /** * Executes the query and gets it's first row. * * @param string $query The SQL query * @param mixed[] $params The query parameters * * @return mixed[] An associative array with the first row, %NULL if error */ function getRow($query, $params = NULL) { $result = $this->query($query, $params); return $this->getRowFromResult($result); } /** * Executes the query and gets it's first row. * * @param string $query The SQL query * @param mixed[] $params The query parameters * * @return object An object with the first row, %NULL if error */ function getObject($query, $params = NULL) { $result = $this->query($query, $params); return $this->getObjectFromResult($result); } /** * Executes the query and gets the first value of the first row. * * @param string $query The SQL query * @param mixed[] $params The query parameters * * @return mixed The value or %NULL if error */ function getValue($query, $params = NULL) { $result = $this->query($query, $params); return $this->getValueFromResult($result); } /** * Loads a query from a file and renders it. * * @param string $file The file path * @param mixed[] $params The query parameters * * @return mixed The query string */ function loadFromFile($file, $params = NULL) { $query = file_get_contents($file .'.sql'); if ($query === FALSE) throw new Exception(NULL, 'Can not load query from file'); return $this->render($query, $params); } /** * Executes the query stored in the specified file and gets the result. * * @param string $file The file path * @param mixed[] $params The query parameters * * @return mixed The value or %NULL if error */ function queryFromFile($file, $params = NULL) { $query = $this->loadFromFile($file, $params); if ($query) return $this->query($query); return NULL; } /** * Executes the file query and gets it's first row. * * @param string $file The file path * @param mixed[] $params The query parameters * * @return mixed[] An associative array with the first row, %NULL if error */ function getRowFromFile($file, $params = NULL) { $result = $this->queryFromFile($file, $params); return $this->getRowFromResult($result); } /** * Executes the file query and gets the first value of the first row. * * @param string $file The file path * @param mixed[] $params The query parameters * * @return mixed The value or %NULL if error */ function getValueFromFile($file, $params = NULL) { $result = $this->queryFromFile($file, $params); return $this->getValueFromResult($result); } /** * Execute multiple queries separated by semicolons. * * @param string $query The SQL query * @param mixed[] $params The query parameters * * @return mixed The value or %NULL if error */ function multiQuery($query, $params = NULL) { $success = $this->conn->multi_query($this->renderDebug($query, $params)); if (!$success) $this->checkError(); return $success; } /** * Prepare an SQL statement for execution. * * @param string $query The SQL query * * @return mixed The statement object or %FALSE if an error occurred */ function prepare($query) { return $this->conn->prepare($query); } function storeResult() { $result = $this->conn->store_result(); if (!$result) $this->checkError(); return $result; } function moreResults() { return $this->conn->more_results(); } function nextResult() { $hasNext = $this->conn->next_result(); $this->checkError(); return $hasNext; } /** * Check if there has been an error in the last query or multiquery, * throwing a @Exception exception if any. */ function checkError() { if ($this->conn->errno) { if ($this->conn->errno != 1644) throw new Exception($this->conn->errno, $this->conn->error); else throw new \Vn\Lib\UserException($this->conn->error, $this->conn->errno); } } /** * Check if there have been warnings in the last query. * * @return boolean %TRUE if there have been warnings %FALSE otherwise */ function checkWarnings() { return $this->conn->warning_count > 0; } /** * Renders an SQL string using the given parameters. * * @param string $query The SQL string * @param mixed[] $paramsMap The query parameters * * @return string The rendered SQL string */ function render($query, $paramsMap = NULL) { if (isset($paramsMap) && is_array($paramsMap) && count($paramsMap) > 0) { $i = 0; $params = []; foreach ($paramsMap as $key => $value) $params[$key] = $this->renderValue($value); $replaceFunc = function($matches) use(&$params, &$i) { $key = substr($matches[0], 1); if (strlen($key) == 0) $key = $i++; if (isset($params[$key])) return $params[$key]; return '#'. $key; }; return preg_replace_callback('/#\w*/', $replaceFunc, $query); } else return $query; } /** * Gets an SQL respresentation from a PHP value. * * @param mixed $value The value * * @return string The SQL value */ function renderValue($value) { if ($value !== NULL) switch (Type::get($value)) { case Type::BOOLEAN: return($value) ? 'TRUE' : 'FALSE'; case Type::STRING: return '\'' . $this->escapeString($value) . '\''; case Type::DATE: return strftime('\'%Y-%m-%d\'', $value->getTimestamp()); case Type::TIME: return strftime('\'%T\'', $value->getTimestamp()); case Type::DATE_TIME: return strftime('\'%Y-%m-%d %T\'', $value->getTimestamp()); default: return '\'' . $this->escapeString($value) . '\''; } else return 'NULL'; } /** * Escapes an string, escaping special characters when necessary. * * @param string $string The string * @return string The escaped string */ function escapeString($string) { return $this->conn->real_escape_string($string); } /** * Quotes an identifier, escaping special characters when necessary. * * @param string $identifier The identifier without quotes * @return string The quoted identifier */ function quote($identifier) { return "`". str_replace("`", "``", $identifier) ."`"; } /** * Renders an SQL string using the given parameters, also debugs the * rendered string if $enableDebug property is set to %true. * * @param string $query The SQL string * @param mixed[] $paramsMap The query parameters * * @return string The rendered SQL string */ function renderDebug($query, $params = NULL) { $renderedQuery = $this->render($query, $params); if ($this->enableDebug) error_log($renderedQuery); return $renderedQuery; } function commit() { return $this->query('COMMIT'); } function rollback() { return $this->query('ROLLBACK'); } function startTransaction() { return $this->query('START TRANSACTION'); } function lastInsertId() { return $this->getValue('SELECT LAST_INSERT_ID()'); } function call($procName, $values) { return $this->query( "CALL {$this->quote($procName)}({$this->renderList($values)})" ); } function insert($table, $values) { return $this->query("INSERT INTO {$this->quote($table)} SET {$this->renderFields($values)}"); } function update($table, $values, $where) { return $this->query("UPDATE {$this->quote($table)} SET {$this->renderFields($values)} WHERE {$this->renderWhere($where)}"); } function renderFields($values) { return $this->renderAssoc($values, ', '); } function renderWhere($operands) { return $this->renderAssoc($operands, ' AND '); } function renderAssoc($list, $separator) { $fields = ''; $isFirst = true; foreach ($list as $field => $value) { if ($isFirst) $isFirst = false; else $fields .= $separator; $fields .= $this->quote($field) .' = '. $this->renderValue($value); } return $fields; } function renderList($list, $separator = ', ') { $values = ''; $isFirst = true; foreach ($list as $value) { if ($isFirst) $isFirst = false; else $values .= $separator; $values .= $this->renderValue($value); } return $values; } }