* @package Mammut\DB\Adapter\MySQL */ class MySQLi extends \Mammut\DB\DB { const _VERSION_ = '1.1.0.0'; const OPT_CLUSTER = 2010; /** * * @var \mysqli */ protected $db = false; protected $type = false; protected $affected = 0; protected $version = 0; public function __construct($cstring, $user, $password, array $options = array(), iCache $cache = NULL) { $this->options = $options; if(!extension_loaded('mysqli')) throw new \Mammut\Exception\ExtensionException('mysqli'); $pers = in_array(self::OPT_PERSISTENT, $options); $this->doQStore = in_array(self::OPT_STORE_QUERIES, $options); if(version_compare(PHP_VERSION, '5.3.0', '<')) $pers = false; if(preg_match('#^([a-zA-Z0-9\.-]+)(:[0-9]+){0,1}/([a-zA-Z0-9_-]+)$#', $cstring, $match)) $this->db = new \mysqli(($pers ? 'p:' : '') . $match[1], $user, $password, $match[3], $match[2] ? str_replace(':', '', $match[2]) : false); // socket connection elseif(preg_match('#^(/[a-zA-Z0-9\./_-]+):([a-zA-Z0-9_-]+)$#', $cstring, $match)) $this->db = @new \mysqli(null, $user, $password, $match[2], false, $match[1]); else throw new \InvalidArgumentException('$cstring is invalid: ' . $cstring); if($this->db->connect_errno != 0) throw new SQLException('Connection failed: ' . $this->db->connect_error, $this->db->connect_errno); $this->dialect = new \Mammut\DB\Sql\Dialect\Mysql($this->db); //$this->common = new MySQLCommon($this, $this->db); $this->db->query('SET SESSION sql_mode = \'ANSI_QUOTES\''); $st = $this->db->get_server_info(); $this->version = substr($st, 0, strpos($st, '.')); if(isset($options['encoding'])) { if(strtolower($options['encoding']) == 'utf-8') $options['encoding'] = 'utf8'; $this->db->query("SET NAMES '{$options['encoding']}'"); $this->db->query("SET CHARACTER SET '{$options['encoding']}'"); } } public function getServer() { return $this->db->host_info; } public function setDatabase($name) { $this->query("USE DATABASE `{$name}`"); } public function getDatabase() { $row = $this->getRow("SELECT DATABASE()"); if(is_array($row)) return $row[0]; else return false; } public function isTransactionSupported() { return $this->version >= 4; } public function setAutocommit($doAutoCommit) { $this->db->autocommit((boolean) $doAutoCommit); } public function startTransaction() { $this->db->query("BEGIN"); } public function commit() { $this->db->commit(); } public function rollback() { $this->db->rollback(); } public function query($query, $limit = -1, $skip = 0) { $query = $this->checkQuery($query); $limit = intval($limit); $skip = intval($skip); if($limit >= 0) { if($skip > 0) $query .= ' LIMIT ' . $skip . ',' . $limit; else $query .= ' LIMIT ' . $limit; } elseif($skip > 0) $query .= ' LIMIT 99999999999999,' . $skip; $this->logTrace('sending new query: ' . $query); $this->affected = 0; $result = false; $this->qcount++; $this->doQStore && $starttime = microtime(true); $result = $this->db->query($query); if($this->db->errno != 0) throw new SQLException($this->db->error, $query, $this->db->errno); $this->doQStore && $this->storeQuery($query, false, microtime(true) - $starttime); if(is_object($result)) return new Result($this->db, $result); else return $result; } public function executeP($query, array $param = array()) { $query = $this->checkQuery($query); $stmtres = $this->db->prepare($query); if($stmtres == false || $this->db->errno != 0) throw new SQLException($this->db->error, $query, $this->db->errno); $data = Statement::prepareData($param); // workaround to the 'type, var, var,...' call, if(count($param) > 0) call_user_func_array(array($stmtres,'bind_param'), $data); $this->doQStore && $starttime = microtime(true); $stmtres->execute(); $this->doQStore && $this->storeQuery($query, $param, microtime(true) - $starttime); if($stmtres->errno != 0) throw new DBException($stmtres->error, $stmtres->errno); $num = $stmtres->affected_rows; $stmtres->close(); return $num; } public function prepareStatement($query, $limit = -1, $skip = 0) { $query = $this->checkQuery($query); $stmtres = $this->db->prepare($query); if($stmtres == false || $this->db->errno != 0) throw new SQLException($this->db->error, $query, $this->db->errno); $stmt = new Statement($this->db, $this->db->prepare($query), $query, $this); return $stmt; } public function getRow($query) { $this->qcount++; $this->doQStore && $starttime = microtime(true); $query = $this->checkQuery($query); $result = $this->db->query($query, 1); $this->doQStore && $this->storeQuery($query, false, microtime(true) - $starttime); if($this->db->errno != 0) throw new SQLException($this->db->error, $query, $this->db->errno); return $result->fetch_row(); } public function getArray($query) { $this->qcount++; $this->doQStore && $starttime = microtime(true); $query = $this->checkQuery($query); $result = $this->db->query($query, 1); $this->doQStore && $this->storeQuery($query, false, microtime(true) - $starttime); if($this->db->errno != 0) throw new SQLException($this->db->error, $query, $this->db->errno); return $result->fetch_assoc(); } public function getObject($query, $class = false, $param = array()) { $this->qcount++; $this->doQStore && $starttime = microtime(true); $query = $this->checkQuery($query); $result = $this->db->query($query, 1); $this->doQStore && $this->storeQuery($query, false, microtime(true) - $starttime); if($this->db->errno != 0) throw new SQLException($this->db->error, $query, $this->db->errno); if(!is_object($result)) throw new DBException('Trying to fetch a result from a resultset free query'); if($class) return $result->fetch_object($class, $param); else return $result->fetch_object(); } public function getInsertId() { return $this->db->insert_id; } /** * * @return int number of rows affected by the last query */ public function getAffectedRowCount() { return $this->db->affected_rows; } public function table($table) { if($table instanceof \Mammut\DB\Model\TableInfo) $table = $table->getName(); $table = strtolower($table); if(array_search($table, $this->tableList()) === false) throw new DBException('table "' . $table . '" dosen\'t exist'); return new Table($this, $table); } public function tableList() { $result = false; $this->qcount++; $result = $this->db->query("SHOW TABLES"); if(is_object($result)) { $tables = array(); while($next = $result->fetch_row()) { $tables[] = $next[0]; } return $tables; } else return false; } public function createTable(\Mammut\DB\Model\TableInfo $info) { // TODO: re-add mysql cluster engine support parent::createTable($info); } public function getTableInfo($tablename) { $tablename = strtolower($tablename); return MySQLUtil::getTableInfo($this, $tablename); } /** * converts a datetime column value to a unix timestap */ public function dateCol2uts($date) { if($date instanceof \DateTime || $date instanceof \Mammut\Date\Date) return $date->format('U'); $match = []; if(preg_match('#([0-9]{1,4})[./-]([0-9]{1,2})[./-]([0-9]{1,2})#', $date, $match)) { $t = gmmktime(0, 0, 0, $match[2], $match[3], $match[1]); return $t; } else return false; } /** * converts a uts integer to the datetime format of this database */ public function uts2dateCol($uts) { return gmdate('Y-m-d', $uts); } /** * converts a datetime column */ public function datetimeCol2uts($datetime) { if($datetime instanceof \DateTime || $datetime instanceof \Mammut\Date\Date) return $datetime->format('U'); $match = []; if(preg_match('#([0-9]{1,4})[./-]([0-9]{1,4})[./-]([0-9]{1,4})\s+([0-9]{1,2})[:.]([0-9]{1,2})[:.]([0-9]{1,2})#', $datetime, $match)) { $t = gmmktime($match[4], $match[5], $match[6], $match[2], $match[3], $match[1]); return $t; } else return false; } /** * converts a uts integer to the datetime format of this database */ public function uts2datetimeCol($uts) { return gmdate('Y-m-d H:i:s', $uts); } public function escapeValue($value, $addQuotes = true) { if(is_null($value)) return 'NULL'; if(is_bool($value)) return $value ? '1' : '0'; if(is_int($value) || is_long($value)) return (int) $value; if(is_float($value) || is_double($value)) return $value; $q = $addQuotes ? '\'' : ''; if(is_string($value)) // if we know its a string, we speed up the escape return $q . $this->db->real_escape_string($value) . $q; // ansi syntax if($value instanceof SQLFunction) return $this->dialect->getFunctionSQL($value); if($value instanceof \DateTime) return $q . $value->format('Y-m-d H:i:s') . $q; if($value instanceof \Mammut\IO\File) { $filename = $value->getPath(); $content = file_get_contents($filename); return $q . $this->db->real_escape_string($content) . $q; } return $q . $this->db->real_escape_string($value) . $q; // ansi syntax } public function getServerVersion() { return $this->db->server_info; } }