connection, 'close')) { mysqli_close($this->connection); } } /** * Connects to the database if needed. * * @return void Returns void if the database connected successfully. * * @throws \RuntimeException */ public function connect() { if ($this->connection) { return; } /* * Unlike mysql_connect(), mysqli_connect() takes the port and socket as separate arguments. Therefore, we * have to extract them from the host string. */ $tmp = substr(strstr($this->options['host'], ':'), 1); if (!empty($tmp)) { // Get the port number or socket name if (is_numeric($tmp)) { $this->options['port'] = $tmp; } else { $this->options['socket'] = $tmp; } // Extract the host name only $this->options['host'] = substr($this->options['host'], 0, strlen($this->options['host']) - (strlen($tmp) + 1)); // This will take care of the following notation: ":3306" if ($this->options['host'] == '') { $this->options['host'] = 'localhost'; } } // Make sure the MySQLi extension for PHP is installed and enabled. if (!function_exists('mysqli_connect')) { throw new \RuntimeException('The MySQL adapter mysqli is not available'); } $this->connection = @mysqli_connect( $this->options['host'], $this->options['user'], $this->options['password'], null, $this->options['port'], $this->options['socket'] ); // Attempt to connect to the server. if (!$this->connection) { throw new \RuntimeException('Could not connect to MySQL.'); } // Set sql_mode to non_strict mode mysqli_query($this->connection, "SET @@SESSION.sql_mode = '';"); // If auto-select is enabled select the given database. if ($this->options['select'] && !empty($this->options['database'])) { $this->select($this->options['database']); } // Set charactersets (needed for MySQL 4.1.2+). $this->setUTF(); } /** * Disconnects the database. * * @return void * */ public function disconnect() { // Close the connection. if (is_callable($this->connection, 'close')) { mysqli_close($this->connection); } $this->connection = null; } /** * Method to escape a string for usage in an SQL statement. * * @param string $text The string to be escaped. * @param boolean $extra Optional parameter to provide extra escaping. * * @return string The escaped string. * */ public function escape($text, $extra = false) { $this->connect(); $result = mysqli_real_escape_string($this->getConnection(), $text); if ($extra) { $result = addcslashes($result, '%_'); } return $result; } /** * Test to see if the MySQL connector is available. * * @return boolean True on success, false otherwise. * */ public static function isSupported() { return (function_exists('mysqli_connect')); } /** * Determines if the connection to the server is active. * * @return boolean True if connected to the database engine. * */ public function connected() { if (is_object($this->connection)) { return mysqli_ping($this->connection); } return false; } /** * Drops a table from the database. * * @param string $tableName The name of the database table to drop. * @param boolean $ifExists Optionally specify that the table must exist before it is dropped. * * @return Mysqli Returns this object to support chaining. * * @throws \RuntimeException */ public function dropTable($tableName, $ifExists = true) { $this->connect(); $query = $this->getQuery(true); $this->setQuery('DROP TABLE ' . ($ifExists ? 'IF EXISTS ' : '') . $query->quoteName($tableName)); $this->execute(); return $this; } /** * Get the number of affected rows for the previous executed SQL statement. * * @return integer The number of affected rows. * */ public function getAffectedRows() { $this->connect(); return mysqli_affected_rows($this->connection); } /** * Method to get the database collation in use by sampling a text field of a table in the database. * * @return mixed The collation in use by the database (string) or boolean false if not supported. * * @throws \RuntimeException */ public function getCollation() { $this->connect(); $tables = $this->getTableList(); $this->setQuery('SHOW FULL COLUMNS FROM ' . $tables[0]); $array = $this->loadAssocList(); foreach ($array as $field) { if (!is_null($field['Collation'])) { return $field['Collation']; } } return null; } /** * Get the number of returned rows for the previous executed SQL statement. * * @param resource $cursor An optional database cursor resource to extract the row count from. * * @return integer The number of returned rows. * */ public function getNumRows($cursor = null) { return mysqli_num_rows($cursor ? $cursor : $this->cursor); } /** * Shows the table CREATE statement that creates the given tables. * * @param mixed $tables A table name or a list of table names. * * @return array A list of the create SQL for the tables. * * @throws \RuntimeException */ public function getTableCreate($tables) { $this->connect(); $result = array(); // Sanitize input to an array and iterate over the list. settype($tables, 'array'); foreach ($tables as $table) { // Set the query to get the table CREATE statement. $this->setQuery('SHOW CREATE table ' . $this->quoteName($this->escape($table))); $row = $this->loadRow(); // Populate the result array based on the create statements. $result[$table] = $row[1]; } return $result; } /** * Retrieves field information about a given table. * * @param string $table The name of the database table. * @param boolean $typeOnly True to only return field types. * * @return array An array of fields for the database table. * * @throws \RuntimeException */ public function getTableColumns($table, $typeOnly = true) { $this->connect(); $result = array(); // Set the query to get the table fields statement. $this->setQuery('SHOW FULL COLUMNS FROM ' . $this->quoteName($this->escape($table))); $fields = $this->loadObjectList(); // If we only want the type as the value add just that to the list. if ($typeOnly) { foreach ($fields as $field) { $result[$field->Field] = preg_replace("/[(0-9)]/", '', $field->Type); } } // If we want the whole field data object add that to the list. else { foreach ($fields as $field) { $result[$field->Field] = $field; } } return $result; } /** * Get the details list of keys for a table. * * @param string $table The name of the table. * * @return array An array of the column specification for the table. * * @throws \RuntimeException */ public function getTableKeys($table) { $this->connect(); // Get the details columns information. $this->setQuery('SHOW KEYS FROM ' . $this->quoteName($table)); $keys = $this->loadObjectList(); return $keys; } /** * Method to get an array of all tables in the database. * * @return array An array of all the tables in the database. * * @throws \RuntimeException */ public function getTableList() { $this->connect(); // Set the query to get the tables statement. $this->setQuery('SHOW TABLES'); $tables = $this->loadColumn(); return $tables; } /** * Get the version of the database connector. * * @return string The database connector version. * */ public function getVersion() { $this->connect(); return mysqli_get_server_info($this->connection); } /** * Method to get the auto-incremented value from the last INSERT statement. * * @return integer The value of the auto-increment field from the last inserted row. * */ public function insertid() { $this->connect(); return mysqli_insert_id($this->connection); } /** * Locks a table in the database. * * @param string $table The name of the table to unlock. * * @return Mysqli Returns this object to support chaining. * * @throws \RuntimeException */ public function lockTable($table) { $this->setQuery('LOCK TABLES ' . $this->quoteName($table) . ' WRITE')->execute(); return $this; } /** * Execute the SQL statement. * * @return mixed A database cursor resource on success, boolean false on failure. * * @throws \RuntimeException */ public function execute() { static $isReconnecting = false; $this->connect(); if (!is_object($this->connection)) { throw new \RuntimeException($this->errorMsg, $this->errorNum); } // Take a local copy so that we don't modify the original query and cause issues later $sql = $this->replacePrefix((string) $this->sql); if ($this->limit > 0 || $this->offset > 0) { $sql .= ' LIMIT ' . $this->offset . ', ' . $this->limit; } // Increment the query counter. $this->count++; // If debugging is enabled then let's log the query. if ($this->debug) { // Add the query to the object queue. $this->log[] = $sql; } // Reset the error values. $this->errorNum = 0; $this->errorMsg = ''; // Execute the query. Error suppression is used here to prevent warnings/notices that the connection has been lost. $this->cursor = @mysqli_query($this->connection, $sql); unset($sql); // If an error occurred handle it. if (!$this->cursor) { $this->errorNum = (int) mysqli_errno($this->connection); $this->errorMsg = (string) mysqli_error($this->connection) . ' SQL=' . $sql; // Check if the server was disconnected. if (!$this->connected() && !$isReconnecting) { $isReconnecting = true; try { // Attempt to reconnect. $this->connection = null; $this->connect(); } // If connect fails, ignore that exception and throw the normal exception. catch (\RuntimeException $e) { throw new \RuntimeException($this->errorMsg, $this->errorNum); } $this->errorNum = null; $this->errorMsg = null; // Since we were able to reconnect, run the query again. $result = $this->execute(); $isReconnecting = false; return $result; } // The server was not disconnected. else { throw new \RuntimeException($this->errorMsg, $this->errorNum); } } return $this->cursor; } /** * Renames a table in the database. * * @param string $oldTable The name of the table to be renamed * @param string $newTable The new name for the table. * @param string $backup Not used by MySQL. * @param string $prefix Not used by MySQL. * * @return Mysqli Returns this object to support chaining. * * @throws \RuntimeException */ public function renameTable($oldTable, $newTable, $backup = null, $prefix = null) { $this->setQuery('RENAME TABLE ' . $oldTable . ' TO ' . $newTable)->execute(); return $this; } /** * Select a database for use. * * @param string $database The name of the database to select for use. * * @return boolean True if the database was successfully selected. * * @throws \RuntimeException */ public function select($database) { $this->connect(); if (!$database) { return false; } if (!mysqli_select_db($this->connection, $database)) { throw new \RuntimeException('Could not connect to database.'); } $this->_database = $database; return true; } /** * Set the connection to use UTF-8 character encoding. * * @return boolean True on success. * */ public function setUTF() { $this->connect(); return $this->connection->set_charset('utf8'); } /** * Method to commit a transaction. * * @return void * * @throws \RuntimeException */ public function transactionCommit() { $this->connect(); $this->setQuery('COMMIT'); $this->execute(); } /** * Method to roll back a transaction. * * @return void * * @throws \RuntimeException */ public function transactionRollback() { $this->connect(); $this->setQuery('ROLLBACK'); $this->execute(); } /** * Method to initialize a transaction. * * @return void * * @throws \RuntimeException */ public function transactionStart() { $this->connect(); $this->setQuery('START TRANSACTION'); $this->execute(); } /** * Method to fetch a row from the result set cursor as an array. * * @param mixed $cursor The optional result set cursor from which to fetch the row. * * @return mixed Either the next row from the result set or false if there are no more rows. * */ protected function fetchArray($cursor = null) { return mysqli_fetch_row($cursor ? $cursor : $this->cursor); } /** * Method to fetch a row from the result set cursor as an associative array. * * @param mixed $cursor The optional result set cursor from which to fetch the row. * * @return mixed Either the next row from the result set or false if there are no more rows. * */ protected function fetchAssoc($cursor = null) { return mysqli_fetch_assoc($cursor ? $cursor : $this->cursor); } /** * Method to fetch a row from the result set cursor as an object. * * @param mixed $cursor The optional result set cursor from which to fetch the row. * @param string $class The class name to use for the returned row object. * * @return mixed Either the next row from the result set or false if there are no more rows. * */ protected function fetchObject($cursor = null, $class = 'stdClass') { return mysqli_fetch_object($cursor ? $cursor : $this->cursor, $class); } /** * Method to free up the memory used for the result set. * * @param mixed $cursor The optional result set cursor from which to fetch the row. * * @return void * */ protected function freeResult($cursor = null) { mysqli_free_result($cursor ? $cursor : $this->cursor); } /** * Unlocks tables in the database. * * @return Mysqli Returns this object to support chaining. * * @throws \RuntimeException */ public function unlockTables() { $this->setQuery('UNLOCK TABLES')->execute(); return $this; } }