* */ class cDb{ var $mtStart; // INTERNAL: The start time, in milliseconds. var $nbQueries; // INTERNAL: The number of executed queries. var $lastResult; // INTERNAL: The last result resource of a query(). var $link; public $history = array(); private $debug = false; public $mysqli = false; /** * @var cDbLog $oDBLog */ public $oDBLog = false; private $bDBLogSkip = false; private $sTransactionSql = ""; private $lTransactionCount = 0; private $lMAxTransactionCount = 0; private $bTransactionActive = false; const OP = 'o'; const VAL = 'v'; const OP_IN = 'IN'; const DEF_WHERE = "1 = 1"; private $oConsole; private static $callCounter = 0; private static $instances = array(); /** * Class singleton constructor * * @author Mareks Keišs * @param bool|string $host - Db host domain or IP * @param bool|string $user - Connection user * @param bool|string $password - Connection user password * @param bool|string $dbName - Database name * @param bool|string $port - Connection port to use or default 3306 * @param string $sKeyAddIn - instance key add-in * * @return cDb $this - instance of cDb class */ public static function getInstance($host = false, $user = false, $password = false, $port = false, $dbName = false, $sKeyAddIn = ''){ if(!($host || $user || $password || $dbName || $port)){ $host = ($host ? $host : DB_HOST); $user = ($user ? $user : DB_USR); $password = ($password ? $password : DB_PWD); $port = ($port ? $port : DB_PORT); $dbName = ($dbName ? $dbName : (defined('COMP_DB_NAME') ? COMP_DB_NAME : DB_NAME)); } // Create key $key = "{$host}|{$user}|{$password}|{$port}|{$dbName}" . ($sKeyAddIn ? "|{$sKeyAddIn}" : ""); // Singleton initiation if(empty(self::$instances[$key])){ self::$instances[$key] = new self($host, $user, $password, $port, $dbName); } self::$callCounter++; return self::$instances[$key]; } /** * Class constructor * * @author Mareks Keišs * @param bool|string $host - Db host domain or IP * @param bool|string $user - Connection user * @param bool|string $password - Connection user password * @param bool|string $dbName - Database name * @param bool|string $port - Connection port to use or default 3306 */ public function __construct($host = false, $user = false, $password = false, $port = false, $dbName = false){ if(!($host || $user || $password || $dbName || $port)){ $host = ($host ? $host : DB_HOST); $user = ($user ? $user : DB_USR); $password = ($password ? $password : DB_PWD); $port = ($port ? $port : DB_PORT); $dbName = ($dbName ? $dbName : (defined('COMP_DB_NAME') ? COMP_DB_NAME : DB_NAME)); } $this->debug = (defined('DB_DEBUG') ? DB_DEBUG : false); $this->mtStart = $this->getMicroTime(); $this->nbQueries = 0; $this->lastResult = NULL; // Establish connection $this->mysqli = new mysqli($host, $user, $password, $dbName, $port); // check connection if($this->mysqli->connect_errno) { printf("Connect failed: %s\n", ($this->debug ? $this->mysqli->connect_error : 'Server connexion not possible.')); exit(); } // Charset $this->mysqli->set_charset("utf8"); // Setup console output object - singleton if($this->debug){ $this->oConsole = cSysConsole::getInstance(); $this->oConsole->addOutputLine(array( 'Connection established', "host: {$host}", "user: {$user}", "password: " . preg_replace("/./im","*",$password), "db: {$dbName}" )); } } /** * Convenient method for mysqli_multi_query(). * * @author Mareks Keišs * @param string $query - The query. * @param bool $bBufferForFurtherUser - buffer query. * @param bool|string $sTable - logger set table for convenience * @param bool|string $sWhere - logger set where statement for data shift * @return Bool result of the query. */ public function executeMulti($query, $bBufferForFurtherUser = false, $sTable = false, $sWhere = false){ $this->queryMulti($query, $sTable, $sWhere); return $this->freeMulti($bBufferForFurtherUser); } private function queryMulti($query, $sTable = false, $sWhere = false){ $iLogId = 0; $this->nbQueries++; // Log query if(!$this->bDBLogSkip && $this->oDBLog) $iLogId = $this->oDBLog->log($query, $sTable, $sWhere); // Exec query $iStart = microtime(true); $this->mysqli->multi_query($query) or $this->debugAndDie($query); $iEnd = microtime(true); // Update exec time if(!$this->bDBLogSkip && $this->oDBLog && $iLogId) $this->oDBLog->updRecordData($iLogId, array('s' => ($iEnd - $iStart))); $this->bDBLogSkip = false; // Save history in debug mode if($this->debug){ $this->history[] = array( 'query' => $query, 'time' => ($iEnd - $iStart) . 's' ); $this->oConsole->addOutputLine(array( 'SQL', "Exec time: " . ($iEnd - $iStart) . "s", "{$query}" ), 'sql'); } return false; } /** * Convenient method for mysqli_query(). * * @author Mareks Keišs * @param string $query - The query. * @param mixed $sTable - logger set table for convenience * @param bool|string $sWhere - logger set where statement for data shift * @return bool|array result of the query. */ public function execute($query, $sTable = false, $sWhere = false){ return $this->query($query, $sTable, $sWhere); } /** * Convenient method for mysqli_query(). * * @author Mareks Keišs * @param string $query - The query. * @param mixed $sTable - logger set table for convenience * @param bool|string $sWhere - logger set where statement for data shift * @return bool|array result of the query. */ private $iLogId = false; public function query($query, $sTable = false, $sWhere = false) { // Check if transaction active if ($this->bTransactionActive) { $this->addTransactionSql($query); return true; } $this->iLogId = 0; $this->nbQueries++; // Log query if (!$this->bDBLogSkip && $this->oDBLog) { $this->iLogId = $this->oDBLog->log($query, $sTable, $sWhere); } // Exec query $iStart = microtime(true); $this->lastResult = $this->mysqli->query($query) or $this->debugAndDie($query); $iEnd = microtime(true); // Update exec time if (!$this->bDBLogSkip && $this->oDBLog && $this->iLogId) $this->oDBLog->updRecordData($this->iLogId, array('s' => ($iEnd - $iStart))); $this->bDBLogSkip = false; // Save history in debug mode if ($this->debug) { $this->history[] = array( 'query' => $query, 'time' => ($iEnd - $iStart) . 's' ); $this->oConsole->addOutputLine(array( 'SQL', "Exec time: " . ($iEnd - $iStart) . "s", "{$query}" ), 'sql'); } return $this->lastResult; } /** * Convenient method for clearing special characters from passed variables * * @author Mareks Keišs * @param mixed $data - data * @param bool $pHtml - preserve html structure * @return mixed $data - clean data */ public function prepData($data, $pHtml = false){ if(is_array($data) && !!$data){ foreach($data as &$v) $v = $this->prepData($v, $pHtml); unset($v); }else if(!!$data){ if($pHtml) $data = $this->mysqli->escape_string(trim($data)); else $data = $this->mysqli->escape_string(htmlspecialchars(trim($data), ENT_QUOTES, 'UTF-8')); } return $data; } /** * Convenient constants for query table field genaration * * @author Mareks Keišs * @category CMS * @version 1.1 */ const TF_TITLE = 'title'; const TF_CTITLE = 'custom_title'; const TF_SORT = 'sort'; const TF_SEARCH = 'search'; const TF_TYPE = 'type'; const TF_T_DATE = 'date'; const TF_T_DROPBOX = 'dropBox'; const TF_T_AJAX_DROPBOX = 'ajaxDropBox'; const TF_T_SELECT = 'select'; const TF_T_INPUT = 'input'; const TF_DROPBOX_DATA = 'data'; const TF_CLASS = 'class'; const TF_DB_FIELD = 'db_field'; const TF_DB_UPDATE_FIELD = 'field'; const TF_BUTTONS = 'btnset'; const TF_DEFAULT_VALUE = 'default'; const TF_S_TYPE = 'searchType'; // SEARCH ARRAY TYPE const TF_ST_BETWEEN = 1; const TF_ST_FROM_TILL = 2; const TF_S_LINK_FIELD = 'field'; // SEARCH ARRAY FIELD const TF_S_OPTIONS = 'options'; // SEARCH ARRAY OPTIONS const TF_SO_TILL = 1; const TF_SO_FROM = 2; /** * Convenient method for building search field query * * @author Mareks Keišs * @category CMS * @param array $data - search value data array * @param array $fields - all available search fields * @param bool $startWithAND - start statement with AND * @param bool $pHtml - preserve html structure * @return string query * @version 1.1 */ public function genSearchFieldQuery(&$data, &$fields, $startWithAND = true, $pHtml = false){ if(count($data) && count($fields)){ $sql = ""; // Construct query foreach($data as $k => $v){ if($v !=='' && isset($fields[$k])){ $v = $this->prepData($v, $pHtml); switch ($fields[$k]['type']) { case self::TF_T_INPUT : if(!empty($fields[$k][self::TF_DB_FIELD])){ if(!is_array($fields[$k][self::TF_DB_FIELD])){ $fields[$k][self::TF_DB_FIELD] = array('0' => $fields[$k][self::TF_DB_FIELD]); } $for = ""; $flink = (!empty($fields[$k]['db_link']) ? $fields[$k]['db_link'] : 'AND'); $operation = (!empty($fields[$k]['operation']) ? $fields[$k]['operation'] : ''); // Search whole words $bWholeWord = false; $vTypes = array('\'', '"'); if(in_array(substr($v, 0, 1), $vTypes) && in_array(substr($v, -1), $vTypes)){ $bWholeWord = true; $v = substr($v, 1, strlen($v)-2); } foreach($fields[$k][self::TF_DB_FIELD] as $db_f){ if($operation){ $for .= "{$flink} {$db_f} {$operation} '{$v}' \n"; }elseif($bWholeWord){ $for .= "{$flink} " . $db_f . " LIKE '{$v}' \n"; }else{ $for .= "{$flink} {$db_f} LIKE '%{$v}%' \n"; } } $sql .= "AND (" . substr($for, strlen($flink)) . ")"; } break; case self::TF_T_SELECT: if(!empty($fields[$k]['data'][$v][self::TF_DB_FIELD])){ $sql .= "AND {$fields[$k]['data'][$v][self::TF_DB_FIELD]} = '{$v}' \n"; }else if(!empty($fields[$k][self::TF_DB_FIELD])){ $sql .= "AND {$fields[$k][self::TF_DB_FIELD]} LIKE '{$v}' \n"; } break; case self::TF_T_DROPBOX: if(!empty($fields[$k]['data'][$v])){ $sql .= "AND {$fields[$k][self::TF_DB_FIELD]} = '{$v}' \n"; } break; case self::TF_T_AJAX_DROPBOX: if(!is_array($fields[$k][self::TF_DB_FIELD])) $fields[$k][self::TF_DB_FIELD] = array('0' => $fields[$k][self::TF_DB_FIELD]); $for = ''; $flink = (!empty($fields[$k]['db_link']) ? $fields[$k]['db_link'] : 'AND'); $likePattern = (!empty($fields[$k]['db_like_pattern']) ? $fields[$k]['db_like_pattern'] : ''); // LIKE '%__VALUE__%' pattern is --> %__VALUE__% $operation = (!empty($fields[$k]['operation']) ? $fields[$k]['operation'] : ''); foreach($fields[$k][self::TF_DB_FIELD] as $db_f){ if($operation){ $for .= "{$flink} {$db_f} {$operation} '{$v}' \n"; }else{ $for .= "{$flink} {$db_f} LIKE " . (strlen($likePattern) ? "'" . str_replace('__VALUE__',$v, $likePattern) . "'" : "'%{$v}%'") . " \n"; } } $sql .= "AND (" . substr($for, strlen($flink)) . ")"; break; case self::TF_T_DATE: $s = $fields[$k]; if(!!$s[self::TF_SEARCH] && is_array($s[self::TF_SEARCH])) $s = &$s[self::TF_SEARCH]; // Check if db fields exists & data fields exists if(!!$s && !empty($s[self::TF_DB_FIELD]) && !empty($s[self::TF_S_LINK_FIELD]) && isset($data[$s[self::TF_S_LINK_FIELD]])){ $sLinkFieldKey = $s[self::TF_S_LINK_FIELD]; $sLinkFieldValue = $data[$sLinkFieldKey]; switch($s[self::TF_S_TYPE]){ case self::TF_ST_BETWEEN: $d1 = strtotime(str_replace("/", "-", $v)); $d2 = strtotime(str_replace("/", "-", $sLinkFieldValue)); if($d1 > $d2){ $t = $d2; $d2 = $d1; $d1 = $t; } // Set oldest date closest border $d2 = strtotime(date("d-m-Y", $d2) . " 23:59:59"); //pr(date('d/m/Y H:i:s', $d1),date('d/m/Y h:i:s', $d2)); unset($data[$sLinkFieldKey]); // Remove linked 2nd field $sql .= "AND ({$s[self::TF_DB_FIELD]} BETWEEN {$d1} AND {$d2}) \n"; break; case self::TF_ST_FROM_TILL: if(isset($s[self::TF_S_OPTIONS]) && $s[self::TF_S_OPTIONS] == self::TF_SO_FROM){ $d1 = strtotime(str_replace("/", "-", $v)); $d2 = strtotime(str_replace("/", "-", $sLinkFieldValue)); }else{ $d1 = strtotime(str_replace("/", "-", $sLinkFieldValue)); $d2 = strtotime(str_replace("/", "-", $v)); } if($d1 > 0 && $d2 > 0){ if($d1 > $d2){ $t = $d2; $d2 = $d1; $d1 = $t; } } // Set oldest date closest border if($d2 > 0) $d2 = strtotime(date("d-m-Y", $d2) . " 23:59:59"); //pr(date('d/m/Y H:i:s', $d1) . "<-- \n",date('d/m/Y h:i:s', $d2) . "<-- \n"); unset($data[$sLinkFieldKey]); // Remove linked 2nd field if($d1 > 0 && $d2 > 0) $sql .= "AND ({$s[self::TF_DB_FIELD]} >= {$d1} OR {$fields[$s[self::TF_S_LINK_FIELD]][self::TF_DB_FIELD]} >= {$d1}) AND ({$fields[$s[self::TF_S_LINK_FIELD]][self::TF_DB_FIELD]} <= {$d2} OR {$s[self::TF_DB_FIELD]} <= {$d2})"; elseif($d1 > 0 && $s[self::TF_S_OPTIONS] == self::TF_SO_FROM) $sql .= "AND ({$s[self::TF_DB_FIELD]} >= {$d1} OR {$fields[$s[self::TF_S_LINK_FIELD]][self::TF_DB_FIELD]} >= {$d1}) "; elseif($d2 > 0 && $s[self::TF_S_OPTIONS] == self::TF_SO_TILL) $sql .= "AND ({$fields[$s[self::TF_S_LINK_FIELD]][self::TF_DB_FIELD]} <= {$d2} OR {$s[self::TF_DB_FIELD]} <= {$d2})"; break; } } break; } } } return $startWithAND ? ' ' . $sql : substr($sql, 3); } } /** * Convenient method for building sort by field query * * @author Mareks Keišs * @category CMS * @param string $sortBy - field key sort by * @param bool $sortWay - field sort order true / false * @param array $fieldData - fields available in table * @return string query * @version 1.1 */ public function genOrderByQuery($sortBy, $sortWay, $fieldData){ if($sortBy && isset($fieldData[$sortBy][self::TF_DB_FIELD]) && !!$fieldData[$sortBy][self::TF_DB_FIELD]){ $sql = ""; $sortWay = ($sortWay ? ' ASC ' : ' DESC '); if(!is_array($fieldData[$sortBy][self::TF_DB_FIELD])) $fieldData[$sortBy][self::TF_DB_FIELD] = array($fieldData[$sortBy][self::TF_DB_FIELD]); foreach($fieldData[$sortBy][self::TF_DB_FIELD] as $dbField){ $sql .= ", {$dbField} {$sortWay}"; } return substr($sql, 1); } return false; } /** * Convenient method for fetching one * * @author Mareks Keišs * @param mixed $vPrepCfg - encode/decode data for display - prepare data if saved in raw html format, or if encoded - decode * @param instance $result - The ressource returned by query() * @return bool true / false or array with data */ public function getOne($vPrepCfg = false, $result = NULL){ if ($result == NULL) $result = $this->lastResult; if($result && $result->num_rows){ $row = $result->fetch_assoc(); $field = array_shift($row); unset($row); }else{ $field = false; } $this->free(); return $this->prepRespData($field, $vPrepCfg); } /** * Convenient method for fetching row * * @author Mareks Keišs * @param mixed $vPrepCfg - encode/decode data for display - prepare data if saved in raw html format, or if encoded - decode * @param instance $result - The resource returned by query() * @return mixed true / false / array with data */ public function getRow($vPrepCfg = false, $result = NULL){ if($result == NULL) $result = $this->lastResult; if($result == NULL || $result->num_rows < 1){ return NULL; }else{ $row = $result->fetch_assoc(); $result->free(); return $this->prepRespData($row, $vPrepCfg); } } /** * Convenient method for fetching an array * * @author Mareks Keišs * @param mixed $vPrepCfg - encode/decode data for display - prepare data if saved in raw html format, or if encoded - decode * @param instance $result - The resource returned by query() * @return bool|array true / false or array with data */ public function getArray($vPrepCfg = false, $result = NULL){ if($result == NULL) $result = $this->lastResult; if($result == NULL || $result->num_rows < 1){ return NULL; }else{ $arr = array(); while($row = $result->fetch_assoc()) $arr[] = $row; unset($row); $this->free($result); return $this->prepRespData($arr, $vPrepCfg); } } /** * Convenient method for fetching an field based array * * @author Mareks Keišs * @param string $field - field by which to group * @param string|bool $subArrField - sub-field by which to group * @param bool $simpleRewrite - simply rewrite each similar id * @param string|bool $fieldKey - resource field to group by * @param mixed $vPrepCfg - encode/decode data for display - prepare data if saved in raw html format, or if encoded - decode * @param instance $result - The resource returned by query() * @return bool|array true / false or array with data */ public function getFieldBasedArray($field, $subArrField = false, $simpleRewrite = true, $fieldKey = false, $vPrepCfg = false, $result = NULL){ if ($result == NULL) $result = $this->lastResult; if ($result == NULL || $result->num_rows < 1) return NULL; else { $arr = array(); while ($row = $result->fetch_assoc()) { if(!$subArrField){ if($simpleRewrite){ $arr[$row[$field]] = ($fieldKey ? @$row[$fieldKey] : $row); }elseif($fieldKey){ $arr[$row[$field]][] = @$row[$fieldKey]; }else{ $arr[$row[$field]][] = $row; } }else{ if($simpleRewrite){ $arr[$row[$field]][$row[$subArrField]] = ($fieldKey ? @$row[$fieldKey] : $row); }elseif($fieldKey){ $arr[$row[$field]][$row[$subArrField]][] = @$row[$fieldKey]; }else{ $arr[$row[$field]][$row[$subArrField]][] = $row; } } } unset($row); $this->free($result); return $this->prepRespData($arr, $vPrepCfg); } } /** * Convenient method for fetching an field based array NEW * * @author Mareks Keišs * @param string $key - field by which to group * @param string|bool $subKey - sub-field by which to group * @param array|string|bool $fields - resource field to select * @param bool $simpleRewrite - simply rewrite each similar id * @param mixed $vPrepCfg - encode/decode data for display - prepare data if saved in raw html format, or if encoded - decode * @param instance $result - The resource returned by query() * @return bool|array true / false or array with data */ public function getFieldBasedArrayFields($key, $subKey = false, $fields = false, $simpleRewrite = true, $vPrepCfg = false, $result = NULL){ if ($result == NULL) $result = $this->lastResult; if ($result == NULL || $result->num_rows < 1) return NULL; else { $arr = array(); while ($row = $result->fetch_assoc()) { if(!$subKey){ if($simpleRewrite){ if($simpleRewrite === true) $arr[$row[$key]] = (!!$fields && is_array($fields) ? array_intersect_key($row, array_flip($fields)) : (isset($row[$fields]) ? $row[$fields] : $row)); else $arr[$row[$key]][$row[$simpleRewrite]] = (!!$fields && is_array($fields) ? array_intersect_key($row, array_flip($fields)) : (isset($row[$fields]) ? $row[$fields] : $row)); }elseif(!!$fields && is_array($fields)){ $arr[$row[$key]][] = array_intersect_key($row, array_flip($fields)); }else{ $arr[$row[$key]][] = (isset($row[$fields]) ? $row[$fields] : $row); } }else{ if($simpleRewrite){ if($simpleRewrite === true) $arr[$row[$key]][$row[$subKey]] = (!!$fields && is_array($fields) ? array_intersect_key($row, array_flip($fields)) : (isset($row[$fields]) ? $row[$fields] : $row)); else $arr[$row[$key]][$row[$subKey]][$row[$simpleRewrite]] = (!!$fields && is_array($fields) ? array_intersect_key($row, array_flip($fields)) : (isset($row[$fields]) ? $row[$fields] : $row)); }elseif(!!$fields && is_array($fields)){ $arr[$row[$key]][$row[$subKey]][] = array_intersect_key($row, array_flip($fields)); }else{ $arr[$row[$key]][$row[$subKey]][] = (isset($row[$fields]) ? $row[$fields] : $row); } } } unset($row); $this->free($result); return $this->prepRespData($arr, $vPrepCfg); } } /** * Convenient method for fetching an field based array advanced & optimized * * @author Mareks Keišs * @param string|array $keys - field/s by which to group * @param array|string|bool $fields - resource field to select * @param bool $simpleRewrite - simply rewrite each similar id * @param mixed $vPrepCfg - encode/decode data for display - prepare data if saved in raw html format, or if encoded - decode * @param instance $result - The resource returned by query() * @return bool|array true / false or array with data */ public function getFieldBasedArrayFieldsAdv($keys, $fields = false, $simpleRewrite = true, $vPrepCfg = false, $result = NULL){ $bArray = false; $keys = (is_array($keys) ? $keys : array($keys)); if(!!$fields && is_array($fields)){ $bArray = true; $flipArray = array_flip($fields); } if ($result == NULL) $result = $this->lastResult; if ($result == NULL || $result->num_rows < 1) return NULL; else { $arr = array(); while ($row = $result->fetch_assoc()){ $ref = &$arr; foreach($keys as $key){ if(!isset($ref[$row[$key]])) $ref[$row[$key]] = array(); $ref = &$ref[$row[$key]]; } if($simpleRewrite){ $ref = ($bArray ? array_intersect_key($row, $flipArray) : ($fields ? @$row[$fields] : $row)); }elseif($bArray){ $ref[] = array_intersect_key($row, $flipArray); }else{ $ref[] = ($fields ? @$row[$fields] : $row); } } unset($row); $this->free($result); return $this->prepRespData($arr, $vPrepCfg); } } /** * Convenient method for manipulating globally data * * @author Mareks Keišs * @param mixed $vData - data to prepare * @param mixed $vPrepData - encode/decode data for display - prepare data if saved in raw html format, or if encoded - decode * @return mixed manipulated data */ const PREP_DATA_ENCODE = 'bPrepData_encode'; const PREP_DATA_DECODE = 'bPrepData_decode'; const PREP_DATA_DECODE_ORIGINAL = 'bPrepData_decode_original'; public function prepRespData($vData = array(), $vPrepData){ if($vPrepData){ if($vPrepData === true) $vPrepData = array('type' => self::PREP_DATA_ENCODE); if(is_string($vPrepData)) $vPrepData = array('type' => $vPrepData); // Data manipulations if(!!$vData && !!$vPrepData){ switch($vPrepData['type']){ case self::PREP_DATA_ENCODE; $vData = $this->prepData($vData); break; case self::PREP_DATA_DECODE; $vData = prepForDisplayHtml($vData); break; case self::PREP_DATA_DECODE_ORIGINAL; $vData = prepForOriginalHtml($vData); break; } } } return $vData; } /** * Convenient method for fetching key => value field based array. * * @author Mareks Keišs * @param string $keyF - result array field key * @param string $valF - result array filed value key * @param mixed $vPrepCfg - encode/decode data for display - prepare data if saved in raw html format, or if encoded - decode * @param instance $result - The resource returned by query() * @return array - containing select data */ public function getKeyValBasedArray($keyF, $valF, $vPrepCfg = false, $result = NULL){ if ($result == NULL) $result = $this->lastResult; if ($result == NULL || $result->num_rows < 1) return NULL; else { $arr = array(); while ($row = $result->fetch_assoc()) { $arr[@$row[$keyF]] = @$row[$valF]; } unset($row); $this->free($result); return $this->prepRespData($arr, $vPrepCfg); } } /** * Convenient method for fetching data & return grouped. * * @author Mareks Keišs * @param string $gr_field - group field * @param string $empty_key - empty other gr field * @param mixed $vPrepCfg - encode/decode data for display - prepare data if saved in raw html format, or if encoded - decode * @param instance $result - The resource returned by query() * @return bool true / false or array with data */ public function getGroupArray($gr_field, $empty_key = 'Others', $vPrepCfg = false, $result = NULL){ if ($result == NULL) $result = $this->lastResult; if ($result == NULL || $result->num_rows < 1) return NULL; else { $arr = array(); $curr_field = ''; while ($row = $result->fetch_assoc()) { if($curr_field != $row[$gr_field]){ $curr_field = $row[$gr_field]; } if(!$curr_field){ $curr_field = $empty_key; } $arr[$curr_field][] = $row; } unset($row); $this->free($result); return $this->prepRespData($arr, $vPrepCfg); } } /** * Convenient method for getting last select found row count * * @author Mareks Keišs * @param bool $debug - indicates debug mode * @return cleared object value */ public function getSelectedRecordsCount($debug = false){ $this->bDBLogSkip = true; $this->query("SELECT FOUND_ROWS()", $debug); return $this->getOne(); } /** * Convenient method for setting logger class * * @author Mareks Keišs * @param mixed $loggerObject - logger class reference * @returns void */ public function setLogger(&$loggerObject){ if(is_callable(array($loggerObject, 'log'))) $this->oDBLog = $loggerObject; } /** * Convenient method for initializing logger class * * @author Mareks Keišs * @param int $iUserId - user id to link to log * @returns void */ public function initLogger($iUserId = 0){ if($iUserId) $this->oDBLog = new cDbLog($iUserId); } /** * Convenient method for mysqli_free_result() * * @author Mareks Keišs * @param instance $result - The resource returned by query() * @return bool true / false or array with data */ public function free($result = NULL){ if ($result == NULL) $result = $this->lastResult; return ($result && is_callable(array($result, 'free'), true) ? $result->free() : false); } private function freeMulti($bBuffer = false){ // flush multi_queries if(!$bBuffer){ while ($this->mysqli->more_results() && $this->mysqli->next_result()){ if (!$this->mysqli->more_results()) break; } }else{ do{ /* store first result set */ if($result = $this->mysqli->store_result()) { /* while($row = $result->fetch_row()) { // printf("%s\n", $row[0]); }*/ $result->free_result(); } /* print divider */ //if($this->mysqli->more_results()) { // printf("-----------------\n"); // } }while($this->mysqli->next_result()); } return true; } /** * Convenient method for getting id of the very last inserted row. * * @author Mareks Keišs * @return int $rowId - the id of the very last inserted row (in any table). */ function lastInsertedId(){ return $this->mysqli->insert_id; } /** * Convenient method for getting affected row count * * @author Mareks Keišs * @return int $rowId - the id of the very last inserted row (in any table). */ function affectedRowCount(){ return $this->mysqli->affected_rows; } /** * Function to get the number of queries executed from the begin of this object * * @author Mareks Keišs * @return int $count - The number of queries executed */ function getQueriesCount(){ return $this->nbQueries; } /** * Function to get current time. * * @author Mareks Keišs * @return int $time - current time in seconds with microseconds (in float format). */ function getMicroTime(){ list($msec, $sec) = explode(' ', microtime()); return floor($sec / 1000) + $msec; } /** * Function to get execution time since beginning of object creation. * * @author Mareks Keišs * @return int $time - execution time in seconds. */ function getExecTime(){ return round(($this->getMicroTime() - $this->mtStart) * 1000) / 1000; } /***************************************************************************************************************** * DEBUG FUNCTIONS * * Internal function to debug when MySQL encountered an error * @author Mareks Keišs * @param string $query The SQL query to echo before dying. */ private function debugAndDie($query){ if($this->debug){ $this->debugQuery($query, "Error"); if(POST_REQ){ die("\n\n" . $this->mysqli->error); }else{ die("

" . $this->mysqli->error . "

"); } } } private function debugQuery($query, $reason = "Debug"){ $color = ($reason == "Error" ? "red" : "orange"); if(POST_REQ){ echo "$reason\n\n" . "$query"; }else{ echo "
" . "

" . "$reason: " . "" . ($this->getFormattedSQL($query)) . "

"; } } private function getFormattedSQL($sql_raw){ if( empty($sql_raw) || !is_string($sql_raw) ) { return false; } $sql_reserved_all = array ( 'ACCESSIBLE', 'ACTION', 'ADD', 'AFTER', 'AGAINST', 'AGGREGATE', 'ALGORITHM', 'ALL', 'ALTER', 'ANALYSE', 'ANALYZE', 'AND', 'AS', 'ASC', 'AUTOCOMMIT', 'AUTO_INCREMENT', 'AVG_ROW_LENGTH', 'BACKUP', 'BEGIN', 'BETWEEN', 'BINLOG', 'BOTH', 'BY', 'CASCADE', 'CASE', 'CHANGE', 'CHANGED', 'CHARSET', 'CHECK', 'CHECKSUM', 'COLLATE', 'COLLATION', 'COLUMN', 'COLUMNS', 'COMMENT', 'COMMIT', 'COMMITTED', 'COMPRESSED', 'CONCURRENT', 'CONSTRAINT', 'CONTAINS', 'CONVERT', 'CREATE', 'CROSS', 'CURRENT_TIMESTAMP', 'DATABASE', 'DATABASES', 'DAY', 'DAY_HOUR', 'DAY_MINUTE', 'DAY_SECOND', 'DEFINER', 'DELAYED', 'DELAY_KEY_WRITE', 'DELETE', 'DESC', 'DESCRIBE', 'DETERMINISTIC', 'DISTINCT', 'DISTINCTROW', 'DIV', 'DO', 'DROP', 'DUMPFILE', 'DUPLICATE', 'DYNAMIC', 'ELSE', 'ENCLOSED', 'END', 'ENGINE', 'ENGINES', 'ESCAPE', 'ESCAPED', 'EVENTS', 'EXECUTE', 'EXISTS', 'EXPLAIN', 'EXTENDED', 'FAST', 'FIELDS', 'FILE', 'FIRST', 'FIXED', 'FLUSH', 'FOR', 'FORCE', 'FOREIGN', 'FROM', 'FULL', 'FULLTEXT', 'FUNCTION', 'GEMINI', 'GEMINI_SPIN_RETRIES', 'GLOBAL', 'GRANT', 'GRANTS', 'GROUP', 'HAVING', 'HEAP', 'HIGH_PRIORITY', 'HOSTS', 'HOUR', 'HOUR_MINUTE', 'HOUR_SECOND', 'IDENTIFIED', 'IF', 'IGNORE', 'IN', 'INDEX', 'INDEXES', 'INFILE', 'INNER', 'INSERT', 'INSERT_ID', 'INSERT_METHOD', 'INTERVAL', 'INTO', 'INVOKER', 'IS', 'ISOLATION', 'JOIN', 'KEY', 'KEYS', 'KILL', 'LAST_INSERT_ID', 'LEADING', 'LEFT', 'LEVEL', 'LIKE', 'LIMIT', 'LINEAR', 'LINES', 'LOAD', 'LOCAL', 'LOCK', 'LOCKS', 'LOGS', 'LOW_PRIORITY', 'MARIA', 'MASTER', 'MASTER_CONNECT_RETRY', 'MASTER_HOST', 'MASTER_LOG_FILE', 'MASTER_LOG_POS', 'MASTER_PASSWORD', 'MASTER_PORT', 'MASTER_USER', 'MATCH', 'MAX_CONNECTIONS_PER_HOUR', 'MAX_QUERIES_PER_HOUR', 'MAX_ROWS', 'MAX_UPDATES_PER_HOUR', 'MAX_USER_CONNECTIONS', 'MEDIUM', 'MERGE', 'MINUTE', 'MINUTE_SECOND', 'MIN_ROWS', 'MODE', 'MODIFY', 'MONTH', 'MRG_MYISAM', 'MYISAM', 'NAMES', 'NATURAL', 'NOT', 'NULL', 'OFFSET', 'ON', 'OPEN', 'OPTIMIZE', 'OPTION', 'OPTIONALLY', 'OR', 'ORDER', 'OUTER', 'OUTFILE', 'PACK_KEYS', 'PAGE', 'PARTIAL', 'PARTITION', 'PARTITIONS', 'PASSWORD', 'PRIMARY', 'PRIVILEGES', 'PROCEDURE', 'PROCESS', 'PROCESSLIST', 'PURGE', 'QUICK', 'RAID0', 'RAID_CHUNKS', 'RAID_CHUNKSIZE', 'RAID_TYPE', 'RANGE', 'READ', 'READ_ONLY', 'READ_WRITE', 'REFERENCES', 'REGEXP', 'RELOAD', 'RENAME', 'REPAIR', 'REPEATABLE', 'REPLACE', 'REPLICATION', 'RESET', 'RESTORE', 'RESTRICT', 'RETURN', 'RETURNS', 'REVOKE', 'RIGHT', 'RLIKE', 'ROLLBACK', 'ROW', 'ROWS', 'ROW_FORMAT', 'SECOND', 'SECURITY', 'SELECT', 'SEPARATOR', 'SERIALIZABLE', 'SESSION', 'SET', 'SHARE', 'SHOW', 'SHUTDOWN', 'SLAVE', 'SONAME', 'SOUNDS', 'SQL', 'SQL_AUTO_IS_NULL', 'SQL_BIG_RESULT', 'SQL_BIG_SELECTS', 'SQL_BIG_TABLES', 'SQL_BUFFER_RESULT', 'SQL_CACHE', 'SQL_CALC_FOUND_ROWS', 'SQL_LOG_BIN', 'SQL_LOG_OFF', 'SQL_LOG_UPDATE', 'SQL_LOW_PRIORITY_UPDATES', 'SQL_MAX_JOIN_SIZE', 'SQL_NO_CACHE', 'SQL_QUOTE_SHOW_CREATE', 'SQL_SAFE_UPDATES', 'SQL_SELECT_LIMIT', 'SQL_SLAVE_SKIP_COUNTER', 'SQL_SMALL_RESULT', 'SQL_WARNINGS', 'START', 'STARTING', 'STATUS', 'STOP', 'STORAGE', 'STRAIGHT_JOIN', 'STRING', 'STRIPED', 'SUPER', 'TABLE', 'TABLES', 'TEMPORARY', 'TERMINATED', 'THEN', 'TO', 'TRAILING', 'TRANSACTIONAL', 'TRUNCATE', 'TYPE', 'TYPES', 'UNCOMMITTED', 'UNION', 'UNIQUE', 'UNLOCK', 'UPDATE', 'USAGE', 'USE', 'USING', 'VALUES', 'VARIABLES', 'VIEW', 'WHEN', 'WHERE', 'WITH', 'WORK', 'WRITE', 'XOR', 'YEAR_MONTH' ); $sql_break_before = array('AND'); $sql_break_after = array('SELECT', 'ON', 'WHERE'); $sql_set_in_bold = array('ON', 'AS', 'AND'); $sql_skip_reserved_words = array('AS', 'ON', 'USING', 'AND'); $sql_special_reserved_words = array('(', ')'); $sql_raw = str_replace("\n", " ", $sql_raw); $sql_formatted = ""; $prev_word = ""; $word = ""; $tab = '     '; for( $i=0, $j = strlen($sql_raw); $i < $j; $i++ ) { $word .= $sql_raw[$i]; $word_trimmed = trim($word); if($sql_raw[$i] == " " || in_array($sql_raw[$i], $sql_special_reserved_words)) { $word_trimmed = trim($word); $trimmed_special = false; if( in_array($sql_raw[$i], $sql_special_reserved_words) ) { $word_trimmed = substr($word_trimmed, 0, -1); $trimmed_special = true; } $word_trimmed = strtoupper($word_trimmed); if(in_array($word_trimmed, $sql_reserved_all) && !in_array($word_trimmed, $sql_skip_reserved_words)){ if(in_array($prev_word, $sql_reserved_all)){ $sql_formatted .= ''.strtoupper(trim($word)).''.' '; }else{ $sql_formatted .= '
 '; $sql_formatted .= ''.strtoupper(trim($word)).''.' '; } $prev_word = $word_trimmed; $word = ""; }else{ if(trim($word)){ // Add line breaks for subfields if(in_array($word_trimmed, $sql_break_before)){ $sql_formatted .= "
{$tab}"; } if(substr($prev_word, strlen($prev_word)-1, 1) == ',' || in_array($prev_word, $sql_break_after)){ $sql_formatted .= "
{$tab}"; } // Set words in bold if(in_array($word_trimmed, $sql_set_in_bold)){ $sql_formatted .= '' . trim($word).' '; }else{ $sql_formatted .= trim($word).' '; } $prev_word = $word_trimmed; $word = ""; } } } } $sql_formatted .= trim($word); return $sql_formatted; } /***************************************************************************************************************** * FAST QUERY REQUEST FUNCTIONS * * * Function to build query based on passed data * * @author Mareks Keišs * @param string $table - table name * @param array|bool $fields - fields to select * @param array|bool $where - where clause * @param array|bool $order - order clause * @return bool true / false */ private function buildQuery($table, $fields = false, $where = false, $order = false){ if (is_array($fields)) { $fields = implode(',', $fields); } else { $fields = "*"; } $where = $this->buildWhereStatement($where); if (is_array($order)) { $d = ''; foreach($order as $k => $v){ $d .= ", " . $k . " " . $this->prepData($v) . " "; } $order = substr($d, 1); } else { $order = " 1 "; } return " SELECT {$fields} FROM {$table} WHERE {$where} ORDER BY {$order}"; } /** * Convenient function for building where statement for query * * @author Mareks Keišs * @param array|bool $where - where clause * @return string - built query */ public function buildWhereStatement($where = false){ if (!!$where && is_array($where)) { $d = ''; foreach($where as $k => $v){ if(is_array($v)){ if(!empty($v[self::OP])){ if(is_array($v[self::VAL])) $d .= "AND `" . $k . "` {$v[self::OP]} ('" . implode("','", $this->prepData($v[self::VAL])) . "') "; else $d .= "AND `" . $k . "` {$v[self::OP]} '" . $this->prepData($v[self::VAL]) . "' "; }else $d .= "AND `" . $k . "` IN ('" . implode("','", $this->prepData($v)) . "') "; }else{ $d .= "AND `" . $k . "` = '" . $this->prepData($v) . "' "; } } $where = substr($d, 3); } else { $where = self::DEF_WHERE; } return $where; } /** * Convenient function for loading row data from db connection by passing query data * * @author Mareks Keišs * @param string $table - table name * @param array|bool $fields - fields to select * @param array|bool $where - where clause * @param array|bool $order - order clause * @return bool|array true / false */ public function _querySelectRow($table, $fields = false, $where = false, $order = false){ if(!!$table){ // Build query $query = $this->buildQuery($table, $fields, $where, $order); $this->query($query, $table); return $this->getRow(); }else{ return null; } } /** * Convenient function for loading array of data from db connection by passing query data * * @author Mareks Keišs * @param string $table - table name * @param array|bool $fields - fields to select * @param array|bool $where - where clause * @param array|bool $order - order clause * @param bool $debug - debug parameter * @param bool $return - return parameter * @return bool|array true / false */ public function _querySelect($table, $fields = false, $where = false, $order = false, $debug = false, $return = true){ if(!!$table){ // Build query $query = $this->buildQuery($table, $fields, $where, $order); $this->query($query, $table); if($return) return $this->getArray(); }else{ return null; } } /** * Convenient function for loading field from db connection by passing query data * * @author Mareks Keišs * @param string $table - table name * @param array|bool $fields - fields to select * @param array|bool $where - where clause * @param array|bool $order - order clause * @return bool true / false */ public function _querySelectOne($table, $fields = false, $where = false, $order = false){ if(!!$table){ // Build query $query = $this->buildQuery($table, $fields, $where, $order); $this->query($query, $table); return $this->getOne(); }else{ return null; } } /** * Convenient method for truncate table * * @author Mareks Keišs * @param string $table - table to truncate * @param bool $debug - indicates debug mode * @return mixed - cleared object value */ public function _truncate($table ,$debug = false){ $query = "TRUNCATE TABLE {$table}"; $this->query($query, $table); return $this->getOne(); } /** * Function to exec insert * * @author Mareks Keišs * @param string $table - table name * @param array $data - fields to insert with key to field structure * @param bool $debug - indicates debug mode * @param bool $pHTML - preserver html * @return int - last inserted id on exec else false */ public function _queryInsert($table, $data = array(), $debug = false, $pHTML = false){ if (count($data)) { $sFields = ''; $sValues = ''; // Escaping insert values foreach ($data as $k => $v) { $sFields .= ",`" . $k . "`"; $sValues .= ",'" . $this->prepData($v, $pHTML) . "'"; } // Create query $query = 'INSERT INTO ' . $table . '(' . substr($sFields,1) . ') VALUES (' . substr($sValues, 1) . ')'; // Exec query if($this->execute($query, $table)) { $vResult = $this->getMultiInsertRowIds(); return array_pop($vResult); } } else { return false; } return false; } /** * Function to exec insert with data array * * @author Mareks Keišs * @param string $table - table name * @param array $rowData - fields to insert with key to field structure * @param bool $debug - indicates debug mode * @param bool $pHTML - preserver html * @return array|bool - array with ids */ public function _queryInsertArray($table, $rowData = array(), $debug = false, $pHTML = false){ if (count($rowData)) { $sFields = ''; $sValues = ''; // Escaping insert values $first_row = true; foreach($rowData as $v) { if(count($v)){ $sRow =''; foreach($v as $kk => $vv) { if($first_row){ $sFields .= ",`" . $kk . "`"; } $sRow .= ",'" . $this->prepData($vv, $pHTML) . "'"; } $sValues .= ',(' . substr($sRow, 1) . ')'; $first_row = false; } } // Create query $query = 'INSERT INTO ' . $table . '(' . substr($sFields,1) . ') VALUES ' . substr($sValues, 1) . ';'; // Exec query if($this->execute($query, $table)){ return $this->getMultiInsertRowIds(count($rowData)); } } else { return false; } return false; } /** * Function to get query || multi-query inserted ids * * @author Mareks Keišs * @param int $iInsertRowsCount - from insert row count * @return array - inserted id on query's */ private function getMultiInsertRowIds($iInsertRowsCount = 0){ $vReturnD = array(); // Check upon inserted rows what to return if($iInsertRowsCount == $this->mysqli->affected_rows){ //Generate id array $i = 0; while($i < $iInsertRowsCount) { $vReturnD[] = $this->mysqli->insert_id + $i++; } }else{ $vReturnD[] = $this->mysqli->insert_id; while($this->mysqli->more_results()){ $this->mysqli->next_result(); $vReturnD[] = $this->mysqli->insert_id; } } // Update log affected ids if(!$this->bDBLogSkip && $this->oDBLog && $this->iLogId){ $this->oDBLog->updRecordData($this->iLogId, array( 'json_data' => cJSON::encode(array('id' => $vReturnD)) )); } return $vReturnD; } /** * Function to exec delete * * @author Mareks Keišs * @param string $table - table name * @param array $where - fields to match before delete * @param bool $debug - indicates debug mode * @return bool - last inserted id on exec else false */ public function _queryDelete($table, $where = array(), $debug = false): bool { if (count($where)) { // Create where clause if (is_array($where) && count($where)) { $d = ''; foreach($where as $k => $v){ if(is_array($v)){ if(!empty($v[self::OP])) $d .= "AND `" . $k . "` {$v[self::OP]} '" . $this->prepData($v[self::VAL]) . "' "; else $d .= "AND `" . $k . "` IN ('" . implode("','", $this->prepData($v)) . "') "; }else $d .= "AND `" . $k . "` = '" . $this->prepData($v) . "' "; } $sWhere = substr($d, 3); // Create query $query = 'DELETE FROM ' . $table . ' WHERE ' . $sWhere; if($this->execute($query, $table, $sWhere)){ return true; } } } else { return false; } return false; } /** * Function to exec update * * @author Mareks Keišs * @param string $table - table name * @param array $data - fields to insert with key to field structure * @param array|bool $where - fields to match the update * @param bool $pHTML - indicates to preserve HTML * @param bool $debug - indicates debug mode * @return bool - last inserted id on exec else false */ public function _queryUpdate($table, $data = array(), $where = false, $pHTML = false, $debug = false): bool { $array = array(); if (count($data)) { $sFields = ''; $sValues = ''; // Escaping update values foreach ($data as $k => $v) $sFields .= ",`" . $k . "` = '" . $this->prepData($v, $pHTML) . "'"; // Create where clause if (is_array($where)){ $d = ''; foreach($where as $k => $v){ if(is_array($v) && count($v)){ if(!empty($v[self::OP])) $d .= "AND `" . $k . "` {$v[self::OP]} '" . $this->prepData($v[self::VAL], $pHTML) . "' "; else $d .= "AND `" . $k . "` IN ('" . implode("','", $this->prepData($v, $pHTML)) . "')"; }else{ $d .= "AND `" . $k . "` = '" . $this->prepData($v, $pHTML) . "'"; } } $sWhere = substr($d, 3); } else { $sWhere = self::DEF_WHERE; } // Create query $query = 'UPDATE ' . $table . ' SET ' . substr($sFields,1) . ' WHERE ' . $sWhere; if($this->execute($query, $table, $sWhere)){ return true; } } else { return false; } return false; } /** * Function to exec update multi with case * * @author Mareks Keišs * @param string $table - table name * @param array $data - fields to insert with key to field structure * @param string $sKey - main key to look in $data array * @param array $where - additional where condition * @param boolean $pHTML - indicates to preserve HTML * @return boolean - true if query executed else false; */ public function _queryUpdateMulti($table, $data = array(), $sKey, $where = array(), $pHTML = false){ $bIsString = false; if (count($data) && $sKey) { $sKey = $this->prepData($sKey, $pHTML); $data = $this->prepData($data, $pHTML); $where = $this->prepData($where, $pHTML); $keys = array($sKey => 1) + (!!$where ? $where : array()); $fields = array(); $ids = array(); foreach($data as $v){ $temp = array_diff_key($v, $keys); // gather all ids; $ids[] = $v[$sKey]; if(!is_numeric($v[$sKey])) $bIsString = true; if(empty($fields)){ $fields = array_diff_key($temp, array()); foreach($fields as &$vv) $vv = " WHEN '{$v[$sKey]}' THEN '{$vv}' "; unset($vv); }else{ foreach($temp as $k=>$vv) $fields[$k] .= " WHEN '{$v[$sKey]}' THEN '{$vv}' "; } unset($vv); } // Create query $sSql = ""; foreach($fields as $k=>$v) $sSql .= ", `{$k}` = CASE `{$sKey}` {$v} END"; $sWhere = "`{$sKey}` IN (". ($bIsString ? "'" . implode("','", $ids) . "'" : implode(",", $ids)) .") "; if(!empty($where)){ foreach($where as $k => $v) $sWhere .= " AND `{$k}` = '{$v}'"; } // Complete query $sSql = "UPDATE `{$table}` SET " . substr($sSql, 1) . " WHERE " . $sWhere; if($this->execute($sSql, $table, $sWhere)) return true; } return false; } /** * Function to start transition * * @author Mareks Keišs * @param int $maxQueriesInBatch - count of transactions in send batch * @return bool - status */ public function startTransaction($maxQueriesInBatch = 20){ $this->lMAxTransactionCount = $maxQueriesInBatch; return $this->bTransactionActive = true; } /** * Function to end transition and exec script * * @author Mareks Keišs * @return bool - status */ public function endTransactionAndExec(){ if($this->bTransactionActive) $this->chkExecSqlTransactionData(true); $this->bTransactionActive = false; return true; } /** * Function to add sql to transaction * * @author Mareks Keišs * @param string $sSql - sql to add to transaction batch * @return bool - status */ public function addTransactionSql($sSql){ if($this->bTransactionActive){ $sSql = trim($sSql); // Add semicolon at the end if none found if(substr($sSql,-1) != ';') $sSql .= ';'; $this->lTransactionCount++; $this->sTransactionSql .= "{$sSql}\n"; // Check if limit reached $this->chkExecSqlTransactionData(); } return true; } /** * Function to check if transaction limit reached and exec sql batch * * @author Mareks Keišs * @param bool $bExec - exec sql data or check limit reach * @return bool - status */ public function chkExecSqlTransactionData($bExec = false){ if($this->bTransactionActive){ if($this->lTransactionCount && strlen($this->sTransactionSql)){ if($bExec || ($this->lTransactionCount >= $this->lMAxTransactionCount)){ if($this->executeMulti($this->sTransactionSql)){ $this->sTransactionSql = ""; $this->lTransactionCount = 0; return true; } } }elseif($bExec){ // Clear data if sth went wrong $this->sTransactionSql = ""; $this->lTransactionCount = 0; return true; } } return false; } }