├── solverlists.db ├── README.md ├── img └── unch.jpg ├── libs ├── waf.php ├── conf.php ├── func.php ├── gen.php └── MysqliDb.php ├── api.php └── index.php /solverlists.db: -------------------------------------------------------------------------------- 1 | viloid -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dump-The-Flag-Source -------------------------------------------------------------------------------- /img/unch.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vsec7/Dump-The-Flag-Source/master/img/unch.jpg -------------------------------------------------------------------------------- /libs/waf.php: -------------------------------------------------------------------------------- 1 | "Thanks For Voting")); 17 | }else{ 18 | header('Content-Type: application/json'); 19 | echo json_encode(array("result" => mysqli_error($con))); 20 | } 21 | unset($_SESSION['cans']); 22 | }else{ 23 | header('Content-Type: application/json'); 24 | echo json_encode(array("result" => "Invalid cans_token")); 25 | } 26 | }else{ 27 | header('Content-Type: application/json'); 28 | echo json_encode(array("result" => "null")); 29 | } 30 | -------------------------------------------------------------------------------- /libs/func.php: -------------------------------------------------------------------------------- 1 | rawQuery($sql)[0][table_name]; 42 | } 43 | 44 | 45 | function column_name($n,$h,$u,$p,$d) 46 | { 47 | $con = new MysqliDb ($h,$u,$p,$d); 48 | $table = table_name($h,$u,$p,$d); 49 | $sql = "SELECT column_name FROM information_schema.columns WHERE table_name='$table' LIMIT $n,1"; 50 | return $con->rawQuery($sql)[0][column_name]; 51 | } 52 | 53 | 54 | function dump_data($tab,$col,$h,$u,$p,$d,$n=1) 55 | { 56 | $con = new MysqliDb ($h,$u,$p,$d); 57 | $sql = "SELECT $col FROM $tab WHERE id=$n"; 58 | return $con->rawQuery($sql)[0][$col]; 59 | 60 | } 61 | -------------------------------------------------------------------------------- /libs/gen.php: -------------------------------------------------------------------------------- 1 | rawQuery("DROP DATABASE IF EXISTS $dbname"); 20 | // Buat Db baru 21 | $con->rawQuery("CREATE database $dbname CHARACTER SET `gbk`"); 22 | // Buat table 23 | $con->rawQuery("CREATE TABLE IF NOT EXISTS $dbname.$rand_table 24 | ( 25 | id INT(5) PRIMARY KEY NOT NULL AUTO_INCREMENT, 26 | vote VARCHAR(250) NOT NULL, 27 | $rand_column CHAR(32) NOT NULL 28 | )"); 29 | 30 | $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; 31 | 32 | 33 | // Buat key 34 | $key = "SHL{".rand_str(12, $chars)."}"; 35 | 36 | $isi = array("1" => "Kebawahhhhh", "2" => "Kebawah Lagi", "3" => "Uhhhh bawahh", "4" => "Terussss", 37 | "5" => "Emmhhhh", "6" => "Teruss kebawah", "7" => "terussss", "8" => "Unchhh", "9" => "mwwaaahhh", "10" => "Capek Belum ?", "11" => "Kebawahhhhh", "12" => "Kebawah Lagi", "13" => "Uhhhh bawahh", "14" => "Terussss", 38 | "15" => "Emmhhhh", "16" => "Teruss kebawah", "17" => "terussss", "18" => "Unchhh", "19" => "mwwaaahhh", "20" => "Capek Belum ?" ); 39 | 40 | for($i=1;$i<=20;$i++){ 41 | $r = rand(0,1); 42 | $con->rawQuery("INSERT INTO $dbname.$rand_table VALUES ($i, '$r', '$isi[$i]')"); 43 | } 44 | $con->rawQuery("INSERT INTO $dbname.$rand_table VALUES ('21', '1', '$key')"); 45 | 46 | } -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | where("vote", 1); 12 | $db->get($tbl); 13 | $a = $db->count; 14 | 15 | // via 16 | $db->where("vote", 0); 17 | $db->get($tbl); 18 | $b = $db->count; 19 | 20 | $all = $a+$b; 21 | 22 | $nella = persen($a,$all); 23 | $via = persen($b,$all); 24 | 25 | $r = base64_encode("If you found valid Flag SHL{r4nd0m1z3str1n6} , Send Your Flag To https://t.me/viloid_bot"); 26 | 27 | ?> 28 | 29 | 30 | DUMP THE FLAG 2 31 | 32 | 33 | 34 | 35 | 36 | 60 | 61 | 62 | 63 |
64 |
65 |

Penyanyi Dangdut Cans

66 |

67 |
68 | 69 | VIA VALLEN 70 | NELLA KHARISMA 71 |
72 | 73 |
74 |

75 | 76 | STATISTIK 77 | 78 | 79 | 80 | 81 |
Total Valid Vote : Jones
Nella Kharisma : ( )
Via Vallen : ( )
82 | 83 |
84 | HALL OF FAME 85 | 86 | 87 | "; 92 | $n++; 93 | } 94 | ?> 95 |
NO Solver Name
$n@$solv
96 |
97 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /libs/MysqliDb.php: -------------------------------------------------------------------------------- 1 | 8 | * @author Josh Campbell 9 | * @author Alexander V. Butenko 10 | * @copyright Copyright (c) 2010-2017 11 | * @license http://opensource.org/licenses/gpl-3.0.html GNU Public License 12 | * @link http://github.com/joshcam/PHP-MySQLi-Database-Class 13 | * @version 2.9.2 14 | */ 15 | 16 | class MysqliDb 17 | { 18 | 19 | /** 20 | * Static instance of self 21 | * @var MysqliDb 22 | */ 23 | protected static $_instance; 24 | 25 | /** 26 | * Table prefix 27 | * @var string 28 | */ 29 | public static $prefix = ''; 30 | 31 | /** 32 | * MySQLi instances 33 | * @var mysqli[] 34 | */ 35 | protected $_mysqli = array(); 36 | 37 | /** 38 | * The SQL query to be prepared and executed 39 | * @var string 40 | */ 41 | protected $_query; 42 | 43 | /** 44 | * The previously executed SQL query 45 | * @var string 46 | */ 47 | protected $_lastQuery; 48 | 49 | /** 50 | * The SQL query options required after SELECT, INSERT, UPDATE or DELETE 51 | * @var array 52 | */ 53 | protected $_queryOptions = array(); 54 | 55 | /** 56 | * An array that holds where joins 57 | * @var array 58 | */ 59 | protected $_join = array(); 60 | 61 | /** 62 | * An array that holds where conditions 63 | * @var array 64 | */ 65 | protected $_where = array(); 66 | 67 | /** 68 | * An array that holds where join ands 69 | * 70 | * @var array 71 | */ 72 | protected $_joinAnd = array(); 73 | 74 | /** 75 | * An array that holds having conditions 76 | * @var array 77 | */ 78 | protected $_having = array(); 79 | 80 | /** 81 | * Dynamic type list for order by condition value 82 | * @var array 83 | */ 84 | protected $_orderBy = array(); 85 | 86 | /** 87 | * Dynamic type list for group by condition value 88 | * @var array 89 | */ 90 | protected $_groupBy = array(); 91 | 92 | /** 93 | * Dynamic type list for tempromary locking tables. 94 | * @var array 95 | */ 96 | protected $_tableLocks = array(); 97 | 98 | /** 99 | * Variable which holds the current table lock method. 100 | * @var string 101 | */ 102 | protected $_tableLockMethod = "READ"; 103 | 104 | /** 105 | * Dynamic array that holds a combination of where condition/table data value types and parameter references 106 | * @var array 107 | */ 108 | protected $_bindParams = array(''); // Create the empty 0 index 109 | 110 | /** 111 | * Variable which holds an amount of returned rows during get/getOne/select queries 112 | * @var string 113 | */ 114 | public $count = 0; 115 | 116 | /** 117 | * Variable which holds an amount of returned rows during get/getOne/select queries with withTotalCount() 118 | * @var string 119 | */ 120 | public $totalCount = 0; 121 | 122 | /** 123 | * Variable which holds last statement error 124 | * @var string 125 | */ 126 | protected $_stmtError; 127 | 128 | /** 129 | * Variable which holds last statement error code 130 | * @var int 131 | */ 132 | protected $_stmtErrno; 133 | 134 | /** 135 | * Is Subquery object 136 | * @var bool 137 | */ 138 | protected $isSubQuery = false; 139 | 140 | /** 141 | * Name of the auto increment column 142 | * @var int 143 | */ 144 | protected $_lastInsertId = null; 145 | 146 | /** 147 | * Column names for update when using onDuplicate method 148 | * @var array 149 | */ 150 | protected $_updateColumns = null; 151 | 152 | /** 153 | * Return type: 'array' to return results as array, 'object' as object 154 | * 'json' as json string 155 | * @var string 156 | */ 157 | public $returnType = 'array'; 158 | 159 | /** 160 | * Should join() results be nested by table 161 | * @var bool 162 | */ 163 | protected $_nestJoin = false; 164 | 165 | /** 166 | * Table name (with prefix, if used) 167 | * @var string 168 | */ 169 | private $_tableName = ''; 170 | 171 | /** 172 | * FOR UPDATE flag 173 | * @var bool 174 | */ 175 | protected $_forUpdate = false; 176 | 177 | /** 178 | * LOCK IN SHARE MODE flag 179 | * @var bool 180 | */ 181 | protected $_lockInShareMode = false; 182 | 183 | /** 184 | * Key field for Map()'ed result array 185 | * @var string 186 | */ 187 | protected $_mapKey = null; 188 | 189 | /** 190 | * Variables for query execution tracing 191 | */ 192 | protected $traceStartQ; 193 | protected $traceEnabled; 194 | protected $traceStripPrefix; 195 | public $trace = array(); 196 | 197 | /** 198 | * Per page limit for pagination 199 | * 200 | * @var int 201 | */ 202 | 203 | public $pageLimit = 20; 204 | /** 205 | * Variable that holds total pages count of last paginate() query 206 | * 207 | * @var int 208 | */ 209 | public $totalPages = 0; 210 | 211 | /** 212 | * @var array connections settings [profile_name=>[same_as_contruct_args]] 213 | */ 214 | protected $connectionsSettings = array(); 215 | /** 216 | * @var string the name of a default (main) mysqli connection 217 | */ 218 | public $defConnectionName = 'default'; 219 | 220 | public $autoReconnect = true; 221 | protected $autoReconnectCount = 0; 222 | 223 | /** 224 | * @var bool Operations in transaction indicator 225 | */ 226 | protected $_transaction_in_progress = false; 227 | 228 | /** 229 | * @param string $host 230 | * @param string $username 231 | * @param string $password 232 | * @param string $db 233 | * @param int $port 234 | * @param string $charset 235 | * @param string $socket 236 | */ 237 | public function __construct($host = null, $username = null, $password = null, $db = null, $port = null, $charset = 'utf8', $socket = null) 238 | { 239 | $isSubQuery = false; 240 | 241 | // if params were passed as array 242 | if (is_array($host)) { 243 | foreach ($host as $key => $val) { 244 | $$key = $val; 245 | } 246 | } 247 | 248 | $this->addConnection('default', array( 249 | 'host' => $host, 250 | 'username' => $username, 251 | 'password' => $password, 252 | 'db' => $db, 253 | 'port' => $port, 254 | 'socket' => $socket, 255 | 'charset' => $charset 256 | )); 257 | 258 | if ($isSubQuery) { 259 | $this->isSubQuery = true; 260 | return; 261 | } 262 | 263 | if (isset($prefix)) { 264 | $this->setPrefix($prefix); 265 | } 266 | 267 | self::$_instance = $this; 268 | } 269 | 270 | /** 271 | * A method to connect to the database 272 | * 273 | * @param null|string $connectionName 274 | * @throws Exception 275 | * @return void 276 | */ 277 | public function connect($connectionName = 'default') 278 | { 279 | if(!isset($this->connectionsSettings[$connectionName])) 280 | throw new Exception('Connection profile not set'); 281 | 282 | $pro = $this->connectionsSettings[$connectionName]; 283 | $params = array_values($pro); 284 | $charset = array_pop($params); 285 | 286 | if ($this->isSubQuery) { 287 | return; 288 | } 289 | 290 | if (empty($pro['host']) && empty($pro['socket'])) { 291 | throw new Exception('MySQL host or socket is not set'); 292 | } 293 | 294 | $mysqlic = new ReflectionClass('mysqli'); 295 | $mysqli = $mysqlic->newInstanceArgs($params); 296 | 297 | if ($mysqli->connect_error) { 298 | throw new Exception('Connect Error ' . $mysqli->connect_errno . ': ' . $mysqli->connect_error, $mysqli->connect_errno); 299 | } 300 | 301 | if (!empty($charset)) { 302 | $mysqli->set_charset($charset); 303 | } 304 | $this->_mysqli[$connectionName] = $mysqli; 305 | } 306 | 307 | public function disconnectAll() 308 | { 309 | foreach (array_keys($this->_mysqli) as $k) { 310 | $this->disconnect($k); 311 | } 312 | } 313 | 314 | /** 315 | * Set the connection name to use in the next query 316 | * @param string $name 317 | * @return $this 318 | * @throws Exception 319 | */ 320 | public function connection($name) 321 | { 322 | if (!isset($this->connectionsSettings[$name])) 323 | throw new Exception('Connection ' . $name . ' was not added.'); 324 | 325 | $this->defConnectionName = $name; 326 | return $this; 327 | } 328 | 329 | /** 330 | * A method to disconnect from the database 331 | * 332 | * @params string $connection connection name to disconnect 333 | * @throws Exception 334 | * @return void 335 | */ 336 | public function disconnect($connection = 'default') 337 | { 338 | if (!isset($this->_mysqli[$connection])) 339 | return; 340 | 341 | $this->_mysqli[$connection]->close(); 342 | unset($this->_mysqli[$connection]); 343 | } 344 | 345 | /** 346 | * Create & store at _mysqli new mysqli instance 347 | * @param string $name 348 | * @param array $params 349 | * @return $this 350 | */ 351 | public function addConnection($name, array $params) 352 | { 353 | $this->connectionsSettings[$name] = array(); 354 | foreach (array('host', 'username', 'password', 'db', 'port', 'socket', 'charset') as $k) { 355 | $prm = isset($params[$k]) ? $params[$k] : null; 356 | 357 | if ($k == 'host') { 358 | if (is_object($prm)) 359 | $this->_mysqli[$name] = $prm; 360 | 361 | if (!is_string($prm)) 362 | $prm = null; 363 | } 364 | $this->connectionsSettings[$name][$k] = $prm; 365 | } 366 | return $this; 367 | } 368 | 369 | /** 370 | * A method to get mysqli object or create it in case needed 371 | * 372 | * @return mysqli 373 | */ 374 | public function mysqli() 375 | { 376 | if (!isset($this->_mysqli[$this->defConnectionName])) { 377 | $this->connect($this->defConnectionName); 378 | } 379 | return $this->_mysqli[$this->defConnectionName]; 380 | } 381 | 382 | /** 383 | * A method of returning the static instance to allow access to the 384 | * instantiated object from within another class. 385 | * Inheriting this class would require reloading connection info. 386 | * 387 | * @uses $db = MySqliDb::getInstance(); 388 | * 389 | * @return MysqliDb Returns the current instance. 390 | */ 391 | public static function getInstance() 392 | { 393 | return self::$_instance; 394 | } 395 | 396 | /** 397 | * Reset states after an execution 398 | * 399 | * @return MysqliDb Returns the current instance. 400 | */ 401 | protected function reset() 402 | { 403 | if ($this->traceEnabled) { 404 | $this->trace[] = array($this->_lastQuery, (microtime(true) - $this->traceStartQ), $this->_traceGetCaller()); 405 | } 406 | 407 | $this->_where = array(); 408 | $this->_having = array(); 409 | $this->_join = array(); 410 | $this->_joinAnd = array(); 411 | $this->_orderBy = array(); 412 | $this->_groupBy = array(); 413 | $this->_bindParams = array(''); // Create the empty 0 index 414 | $this->_query = null; 415 | $this->_queryOptions = array(); 416 | $this->returnType = 'array'; 417 | $this->_nestJoin = false; 418 | $this->_forUpdate = false; 419 | $this->_lockInShareMode = false; 420 | $this->_tableName = ''; 421 | $this->_lastInsertId = null; 422 | $this->_updateColumns = null; 423 | $this->_mapKey = null; 424 | if(!$this->_transaction_in_progress ) { 425 | $this->defConnectionName = 'default'; 426 | } 427 | $this->autoReconnectCount = 0; 428 | return $this; 429 | } 430 | 431 | /** 432 | * Helper function to create dbObject with JSON return type 433 | * 434 | * @return MysqliDb 435 | */ 436 | public function jsonBuilder() 437 | { 438 | $this->returnType = 'json'; 439 | return $this; 440 | } 441 | 442 | /** 443 | * Helper function to create dbObject with array return type 444 | * Added for consistency as thats default output type 445 | * 446 | * @return MysqliDb 447 | */ 448 | public function arrayBuilder() 449 | { 450 | $this->returnType = 'array'; 451 | return $this; 452 | } 453 | 454 | /** 455 | * Helper function to create dbObject with object return type. 456 | * 457 | * @return MysqliDb 458 | */ 459 | public function objectBuilder() 460 | { 461 | $this->returnType = 'object'; 462 | return $this; 463 | } 464 | 465 | /** 466 | * Method to set a prefix 467 | * 468 | * @param string $prefix Contains a tableprefix 469 | * 470 | * @return MysqliDb 471 | */ 472 | public function setPrefix($prefix = '') 473 | { 474 | self::$prefix = $prefix; 475 | return $this; 476 | } 477 | 478 | /** 479 | * Pushes a unprepared statement to the mysqli stack. 480 | * WARNING: Use with caution. 481 | * This method does not escape strings by default so make sure you'll never use it in production. 482 | * 483 | * @author Jonas Barascu 484 | * @param [[Type]] $query [[Description]] 485 | */ 486 | private function queryUnprepared($query) 487 | { 488 | // Execute query 489 | $stmt = $this->mysqli()->query($query); 490 | 491 | // Failed? 492 | if ($stmt !== false) 493 | return $stmt; 494 | 495 | if ($this->mysqli()->errno === 2006 && $this->autoReconnect === true && $this->autoReconnectCount === 0) { 496 | $this->connect($this->defConnectionName); 497 | $this->autoReconnectCount++; 498 | return $this->queryUnprepared($query); 499 | } 500 | 501 | throw new Exception(sprintf('Unprepared Query Failed, ERRNO: %u (%s)', $this->mysqli()->errno, $this->mysqli()->error), $this->mysqli()->errno); 502 | } 503 | 504 | /** 505 | * Execute raw SQL query. 506 | * 507 | * @param string $query User-provided query to execute. 508 | * @param array $bindParams Variables array to bind to the SQL statement. 509 | * 510 | * @return array Contains the returned rows from the query. 511 | */ 512 | public function rawQuery($query, $bindParams = null) 513 | { 514 | $params = array(''); // Create the empty 0 index 515 | $this->_query = $query; 516 | $stmt = $this->_prepareQuery(); 517 | 518 | if (is_array($bindParams) === true) { 519 | foreach ($bindParams as $prop => $val) { 520 | $params[0] .= $this->_determineType($val); 521 | array_push($params, $bindParams[$prop]); 522 | } 523 | 524 | call_user_func_array(array($stmt, 'bind_param'), $this->refValues($params)); 525 | } 526 | 527 | $stmt->execute(); 528 | $this->count = $stmt->affected_rows; 529 | $this->_stmtError = $stmt->error; 530 | $this->_stmtErrno = $stmt->errno; 531 | $this->_lastQuery = $this->replacePlaceHolders($this->_query, $params); 532 | $res = $this->_dynamicBindResults($stmt); 533 | $this->reset(); 534 | 535 | return $res; 536 | } 537 | 538 | /** 539 | * Helper function to execute raw SQL query and return only 1 row of results. 540 | * Note that function do not add 'limit 1' to the query by itself 541 | * Same idea as getOne() 542 | * 543 | * @param string $query User-provided query to execute. 544 | * @param array $bindParams Variables array to bind to the SQL statement. 545 | * 546 | * @return array|null Contains the returned row from the query. 547 | */ 548 | public function rawQueryOne($query, $bindParams = null) 549 | { 550 | $res = $this->rawQuery($query, $bindParams); 551 | if (is_array($res) && isset($res[0])) { 552 | return $res[0]; 553 | } 554 | 555 | return null; 556 | } 557 | 558 | /** 559 | * Helper function to execute raw SQL query and return only 1 column of results. 560 | * If 'limit 1' will be found, then string will be returned instead of array 561 | * Same idea as getValue() 562 | * 563 | * @param string $query User-provided query to execute. 564 | * @param array $bindParams Variables array to bind to the SQL statement. 565 | * 566 | * @return mixed Contains the returned rows from the query. 567 | */ 568 | public function rawQueryValue($query, $bindParams = null) 569 | { 570 | $res = $this->rawQuery($query, $bindParams); 571 | if (!$res) { 572 | return null; 573 | } 574 | 575 | $limit = preg_match('/limit\s+1;?$/i', $query); 576 | $key = key($res[0]); 577 | if (isset($res[0][$key]) && $limit == true) { 578 | return $res[0][$key]; 579 | } 580 | 581 | $newRes = Array(); 582 | for ($i = 0; $i < $this->count; $i++) { 583 | $newRes[] = $res[$i][$key]; 584 | } 585 | return $newRes; 586 | } 587 | 588 | /** 589 | * A method to perform select query 590 | * 591 | * @param string $query Contains a user-provided select query. 592 | * @param int|array $numRows Array to define SQL limit in format Array ($offset, $count) 593 | * 594 | * @return array Contains the returned rows from the query. 595 | */ 596 | public function query($query, $numRows = null) 597 | { 598 | $this->_query = $query; 599 | $stmt = $this->_buildQuery($numRows); 600 | $stmt->execute(); 601 | $this->_stmtError = $stmt->error; 602 | $this->_stmtErrno = $stmt->errno; 603 | $res = $this->_dynamicBindResults($stmt); 604 | $this->reset(); 605 | 606 | return $res; 607 | } 608 | 609 | /** 610 | * This method allows you to specify multiple (method chaining optional) options for SQL queries. 611 | * 612 | * @uses $MySqliDb->setQueryOption('name'); 613 | * 614 | * @param string|array $options The optons name of the query. 615 | * 616 | * @throws Exception 617 | * @return MysqliDb 618 | */ 619 | public function setQueryOption($options) 620 | { 621 | $allowedOptions = Array('ALL', 'DISTINCT', 'DISTINCTROW', 'HIGH_PRIORITY', 'STRAIGHT_JOIN', 'SQL_SMALL_RESULT', 622 | 'SQL_BIG_RESULT', 'SQL_BUFFER_RESULT', 'SQL_CACHE', 'SQL_NO_CACHE', 'SQL_CALC_FOUND_ROWS', 623 | 'LOW_PRIORITY', 'IGNORE', 'QUICK', 'MYSQLI_NESTJOIN', 'FOR UPDATE', 'LOCK IN SHARE MODE'); 624 | 625 | if (!is_array($options)) { 626 | $options = Array($options); 627 | } 628 | 629 | foreach ($options as $option) { 630 | $option = strtoupper($option); 631 | if (!in_array($option, $allowedOptions)) { 632 | throw new Exception('Wrong query option: ' . $option); 633 | } 634 | 635 | if ($option == 'MYSQLI_NESTJOIN') { 636 | $this->_nestJoin = true; 637 | } elseif ($option == 'FOR UPDATE') { 638 | $this->_forUpdate = true; 639 | } elseif ($option == 'LOCK IN SHARE MODE') { 640 | $this->_lockInShareMode = true; 641 | } else { 642 | $this->_queryOptions[] = $option; 643 | } 644 | } 645 | 646 | return $this; 647 | } 648 | 649 | /** 650 | * Function to enable SQL_CALC_FOUND_ROWS in the get queries 651 | * 652 | * @return MysqliDb 653 | */ 654 | public function withTotalCount() 655 | { 656 | $this->setQueryOption('SQL_CALC_FOUND_ROWS'); 657 | return $this; 658 | } 659 | 660 | /** 661 | * A convenient SELECT * function. 662 | * 663 | * @param string $tableName The name of the database table to work with. 664 | * @param int|array $numRows Array to define SQL limit in format Array ($offset, $count) 665 | * or only $count 666 | * @param string $columns Desired columns 667 | * 668 | * @return array Contains the returned rows from the select query. 669 | */ 670 | public function get($tableName, $numRows = null, $columns = '*') 671 | { 672 | if (empty($columns)) { 673 | $columns = '*'; 674 | } 675 | 676 | $column = is_array($columns) ? implode(', ', $columns) : $columns; 677 | 678 | if (strpos($tableName, '.') === false) { 679 | $this->_tableName = self::$prefix . $tableName; 680 | } else { 681 | $this->_tableName = $tableName; 682 | } 683 | 684 | $this->_query = 'SELECT ' . implode(' ', $this->_queryOptions) . ' ' . 685 | $column . " FROM " . $this->_tableName; 686 | $stmt = $this->_buildQuery($numRows); 687 | 688 | if ($this->isSubQuery) { 689 | return $this; 690 | } 691 | 692 | $stmt->execute(); 693 | $this->_stmtError = $stmt->error; 694 | $this->_stmtErrno = $stmt->errno; 695 | $res = $this->_dynamicBindResults($stmt); 696 | $this->reset(); 697 | 698 | return $res; 699 | } 700 | 701 | /** 702 | * A convenient SELECT * function to get one record. 703 | * 704 | * @param string $tableName The name of the database table to work with. 705 | * @param string $columns Desired columns 706 | * 707 | * @return array Contains the returned rows from the select query. 708 | */ 709 | public function getOne($tableName, $columns = '*') 710 | { 711 | $res = $this->get($tableName, 1, $columns); 712 | 713 | if ($res instanceof MysqliDb) { 714 | return $res; 715 | } elseif (is_array($res) && isset($res[0])) { 716 | return $res[0]; 717 | } elseif ($res) { 718 | return $res; 719 | } 720 | 721 | return null; 722 | } 723 | 724 | /** 725 | * A convenient SELECT COLUMN function to get a single column value from one row 726 | * 727 | * @param string $tableName The name of the database table to work with. 728 | * @param string $column The desired column 729 | * @param int $limit Limit of rows to select. Use null for unlimited..1 by default 730 | * 731 | * @return mixed Contains the value of a returned column / array of values 732 | */ 733 | public function getValue($tableName, $column, $limit = 1) 734 | { 735 | $res = $this->ArrayBuilder()->get($tableName, $limit, "{$column} AS retval"); 736 | 737 | if (!$res) { 738 | return null; 739 | } 740 | 741 | if ($limit == 1) { 742 | if (isset($res[0]["retval"])) { 743 | return $res[0]["retval"]; 744 | } 745 | return null; 746 | } 747 | 748 | $newRes = Array(); 749 | for ($i = 0; $i < $this->count; $i++) { 750 | $newRes[] = $res[$i]['retval']; 751 | } 752 | return $newRes; 753 | } 754 | 755 | /** 756 | * Insert method to add new row 757 | * 758 | * @param string $tableName The name of the table. 759 | * @param array $insertData Data containing information for inserting into the DB. 760 | * 761 | * @return bool Boolean indicating whether the insert query was completed succesfully. 762 | */ 763 | public function insert($tableName, $insertData) 764 | { 765 | return $this->_buildInsert($tableName, $insertData, 'INSERT'); 766 | } 767 | 768 | /** 769 | * Insert method to add several rows at once 770 | * 771 | * @param string $tableName The name of the table. 772 | * @param array $multiInsertData Two-dimensinal Data-array containing information for inserting into the DB. 773 | * @param array $dataKeys Optinal Table Key names, if not set in insertDataSet. 774 | * 775 | * @return bool|array Boolean indicating the insertion failed (false), else return id-array ([int]) 776 | */ 777 | public function insertMulti($tableName, array $multiInsertData, array $dataKeys = null) 778 | { 779 | // only auto-commit our inserts, if no transaction is currently running 780 | $autoCommit = (isset($this->_transaction_in_progress) ? !$this->_transaction_in_progress : true); 781 | $ids = array(); 782 | 783 | if($autoCommit) { 784 | $this->startTransaction(); 785 | } 786 | 787 | foreach ($multiInsertData as $insertData) { 788 | if($dataKeys !== null) { 789 | // apply column-names if given, else assume they're already given in the data 790 | $insertData = array_combine($dataKeys, $insertData); 791 | } 792 | 793 | $id = $this->insert($tableName, $insertData); 794 | if(!$id) { 795 | if($autoCommit) { 796 | $this->rollback(); 797 | } 798 | return false; 799 | } 800 | $ids[] = $id; 801 | } 802 | 803 | if($autoCommit) { 804 | $this->commit(); 805 | } 806 | 807 | return $ids; 808 | } 809 | 810 | /** 811 | * Replace method to add new row 812 | * 813 | * @param string $tableName The name of the table. 814 | * @param array $insertData Data containing information for inserting into the DB. 815 | * 816 | * @return bool Boolean indicating whether the insert query was completed succesfully. 817 | */ 818 | public function replace($tableName, $insertData) 819 | { 820 | return $this->_buildInsert($tableName, $insertData, 'REPLACE'); 821 | } 822 | 823 | /** 824 | * A convenient function that returns TRUE if exists at least an element that 825 | * satisfy the where condition specified calling the "where" method before this one. 826 | * 827 | * @param string $tableName The name of the database table to work with. 828 | * 829 | * @return bool 830 | */ 831 | public function has($tableName) 832 | { 833 | $this->getOne($tableName, '1'); 834 | return $this->count >= 1; 835 | } 836 | 837 | /** 838 | * Update query. Be sure to first call the "where" method. 839 | * 840 | * @param string $tableName The name of the database table to work with. 841 | * @param array $tableData Array of data to update the desired row. 842 | * @param int $numRows Limit on the number of rows that can be updated. 843 | * 844 | * @return bool 845 | */ 846 | public function update($tableName, $tableData, $numRows = null) 847 | { 848 | if ($this->isSubQuery) { 849 | return; 850 | } 851 | 852 | $this->_query = "UPDATE " . self::$prefix . $tableName; 853 | 854 | $stmt = $this->_buildQuery($numRows, $tableData); 855 | $status = $stmt->execute(); 856 | $this->reset(); 857 | $this->_stmtError = $stmt->error; 858 | $this->_stmtErrno = $stmt->errno; 859 | $this->count = $stmt->affected_rows; 860 | 861 | return $status; 862 | } 863 | 864 | /** 865 | * Delete query. Call the "where" method first. 866 | * 867 | * @param string $tableName The name of the database table to work with. 868 | * @param int|array $numRows Array to define SQL limit in format Array ($offset, $count) 869 | * or only $count 870 | * 871 | * @return bool Indicates success. 0 or 1. 872 | */ 873 | public function delete($tableName, $numRows = null) 874 | { 875 | if ($this->isSubQuery) { 876 | return; 877 | } 878 | 879 | $table = self::$prefix . $tableName; 880 | 881 | if (count($this->_join)) { 882 | $this->_query = "DELETE " . preg_replace('/.* (.*)/', '$1', $table) . " FROM " . $table; 883 | } else { 884 | $this->_query = "DELETE FROM " . $table; 885 | } 886 | 887 | $stmt = $this->_buildQuery($numRows); 888 | $stmt->execute(); 889 | $this->_stmtError = $stmt->error; 890 | $this->_stmtErrno = $stmt->errno; 891 | $this->reset(); 892 | 893 | return ($stmt->affected_rows > -1); // affected_rows returns 0 if nothing matched where statement, or required updating, -1 if error 894 | } 895 | 896 | /** 897 | * This method allows you to specify multiple (method chaining optional) AND WHERE statements for SQL queries. 898 | * 899 | * @uses $MySqliDb->where('id', 7)->where('title', 'MyTitle'); 900 | * 901 | * @param string $whereProp The name of the database field. 902 | * @param mixed $whereValue The value of the database field. 903 | * @param string $operator Comparison operator. Default is = 904 | * @param string $cond Condition of where statement (OR, AND) 905 | * 906 | * @return MysqliDb 907 | */ 908 | public function where($whereProp, $whereValue = 'DBNULL', $operator = '=', $cond = 'AND') 909 | { 910 | // forkaround for an old operation api 911 | if (is_array($whereValue) && ($key = key($whereValue)) != "0") { 912 | $operator = $key; 913 | $whereValue = $whereValue[$key]; 914 | } 915 | 916 | if (count($this->_where) == 0) { 917 | $cond = ''; 918 | } 919 | 920 | $this->_where[] = array($cond, $whereProp, $operator, $whereValue); 921 | return $this; 922 | } 923 | 924 | /** 925 | * This function store update column's name and column name of the 926 | * autoincrement column 927 | * 928 | * @param array $updateColumns Variable with values 929 | * @param string $lastInsertId Variable value 930 | * 931 | * @return MysqliDb 932 | */ 933 | public function onDuplicate($updateColumns, $lastInsertId = null) 934 | { 935 | $this->_lastInsertId = $lastInsertId; 936 | $this->_updateColumns = $updateColumns; 937 | return $this; 938 | } 939 | 940 | /** 941 | * This method allows you to specify multiple (method chaining optional) OR WHERE statements for SQL queries. 942 | * 943 | * @uses $MySqliDb->orWhere('id', 7)->orWhere('title', 'MyTitle'); 944 | * 945 | * @param string $whereProp The name of the database field. 946 | * @param mixed $whereValue The value of the database field. 947 | * @param string $operator Comparison operator. Default is = 948 | * 949 | * @return MysqliDb 950 | */ 951 | public function orWhere($whereProp, $whereValue = 'DBNULL', $operator = '=') 952 | { 953 | return $this->where($whereProp, $whereValue, $operator, 'OR'); 954 | } 955 | 956 | /** 957 | * This method allows you to specify multiple (method chaining optional) AND HAVING statements for SQL queries. 958 | * 959 | * @uses $MySqliDb->having('SUM(tags) > 10') 960 | * 961 | * @param string $havingProp The name of the database field. 962 | * @param mixed $havingValue The value of the database field. 963 | * @param string $operator Comparison operator. Default is = 964 | * 965 | * @return MysqliDb 966 | */ 967 | 968 | public function having($havingProp, $havingValue = 'DBNULL', $operator = '=', $cond = 'AND') 969 | { 970 | // forkaround for an old operation api 971 | if (is_array($havingValue) && ($key = key($havingValue)) != "0") { 972 | $operator = $key; 973 | $havingValue = $havingValue[$key]; 974 | } 975 | 976 | if (count($this->_having) == 0) { 977 | $cond = ''; 978 | } 979 | 980 | $this->_having[] = array($cond, $havingProp, $operator, $havingValue); 981 | return $this; 982 | } 983 | 984 | /** 985 | * This method allows you to specify multiple (method chaining optional) OR HAVING statements for SQL queries. 986 | * 987 | * @uses $MySqliDb->orHaving('SUM(tags) > 10') 988 | * 989 | * @param string $havingProp The name of the database field. 990 | * @param mixed $havingValue The value of the database field. 991 | * @param string $operator Comparison operator. Default is = 992 | * 993 | * @return MysqliDb 994 | */ 995 | public function orHaving($havingProp, $havingValue = null, $operator = null) 996 | { 997 | return $this->having($havingProp, $havingValue, $operator, 'OR'); 998 | } 999 | 1000 | /** 1001 | * This method allows you to concatenate joins for the final SQL statement. 1002 | * 1003 | * @uses $MySqliDb->join('table1', 'field1 <> field2', 'LEFT') 1004 | * 1005 | * @param string $joinTable The name of the table. 1006 | * @param string $joinCondition the condition. 1007 | * @param string $joinType 'LEFT', 'INNER' etc. 1008 | * 1009 | * @throws Exception 1010 | * @return MysqliDb 1011 | */ 1012 | public function join($joinTable, $joinCondition, $joinType = '') 1013 | { 1014 | $allowedTypes = array('LEFT', 'RIGHT', 'OUTER', 'INNER', 'LEFT OUTER', 'RIGHT OUTER', 'NATURAL'); 1015 | $joinType = strtoupper(trim($joinType)); 1016 | 1017 | if ($joinType && !in_array($joinType, $allowedTypes)) { 1018 | throw new Exception('Wrong JOIN type: ' . $joinType); 1019 | } 1020 | 1021 | if (!is_object($joinTable)) { 1022 | $joinTable = self::$prefix . $joinTable; 1023 | } 1024 | 1025 | $this->_join[] = Array($joinType, $joinTable, $joinCondition); 1026 | 1027 | return $this; 1028 | } 1029 | 1030 | 1031 | /** 1032 | * This is a basic method which allows you to import raw .CSV data into a table 1033 | * Please check out http://dev.mysql.com/doc/refman/5.7/en/load-data.html for a valid .csv file. 1034 | 1035 | * @author Jonas Barascu (Noneatme) 1036 | * @param string $importTable The database table where the data will be imported into. 1037 | * @param string $importFile The file to be imported. Please use double backslashes \\ and make sure you 1038 | * @param string $importSettings An Array defining the import settings as described in the README.md 1039 | * @return boolean 1040 | */ 1041 | public function loadData($importTable, $importFile, $importSettings = null) 1042 | { 1043 | // We have to check if the file exists 1044 | if(!file_exists($importFile)) { 1045 | // Throw an exception 1046 | throw new Exception("importCSV -> importFile ".$importFile." does not exists!"); 1047 | return; 1048 | } 1049 | 1050 | // Define the default values 1051 | // We will merge it later 1052 | $settings = Array("fieldChar" => ';', "lineChar" => PHP_EOL, "linesToIgnore" => 1); 1053 | 1054 | // Check the import settings 1055 | if(gettype($importSettings) == "array") { 1056 | // Merge the default array with the custom one 1057 | $settings = array_merge($settings, $importSettings); 1058 | } 1059 | 1060 | // Add the prefix to the import table 1061 | $table = self::$prefix . $importTable; 1062 | 1063 | // Add 1 more slash to every slash so maria will interpret it as a path 1064 | $importFile = str_replace("\\", "\\\\", $importFile); 1065 | 1066 | // Switch between LOAD DATA and LOAD DATA LOCAL 1067 | $loadDataLocal = isset($settings["loadDataLocal"]) ? 'LOCAL' : ''; 1068 | 1069 | // Build SQL Syntax 1070 | $sqlSyntax = sprintf('LOAD DATA %s INFILE \'%s\' INTO TABLE %s', 1071 | $loadDataLocal, $importFile, $table); 1072 | 1073 | // FIELDS 1074 | $sqlSyntax .= sprintf(' FIELDS TERMINATED BY \'%s\'', $settings["fieldChar"]); 1075 | if(isset($settings["fieldEnclosure"])) { 1076 | $sqlSyntax .= sprintf(' ENCLOSED BY \'%s\'', $settings["fieldEnclosure"]); 1077 | } 1078 | 1079 | // LINES 1080 | $sqlSyntax .= sprintf(' LINES TERMINATED BY \'%s\'', $settings["lineChar"]); 1081 | if(isset($settings["lineStarting"])) { 1082 | $sqlSyntax .= sprintf(' STARTING BY \'%s\'', $settings["lineStarting"]); 1083 | } 1084 | 1085 | // IGNORE LINES 1086 | $sqlSyntax .= sprintf(' IGNORE %d LINES', $settings["linesToIgnore"]); 1087 | 1088 | // Exceute the query unprepared because LOAD DATA only works with unprepared statements. 1089 | $result = $this->queryUnprepared($sqlSyntax); 1090 | 1091 | // Are there rows modified? 1092 | // Let the user know if the import failed / succeeded 1093 | return (bool) $result; 1094 | } 1095 | 1096 | /** 1097 | * This method is usefull for importing XML files into a specific table. 1098 | * Check out the LOAD XML syntax for your MySQL server. 1099 | * 1100 | * @author Jonas Barascu 1101 | * @param string $importTable The table in which the data will be imported to. 1102 | * @param string $importFile The file which contains the .XML data. 1103 | * @param string $importSettings An Array defining the import settings as described in the README.md 1104 | * 1105 | * @return boolean Returns true if the import succeeded, false if it failed. 1106 | */ 1107 | public function loadXml($importTable, $importFile, $importSettings = null) 1108 | { 1109 | // We have to check if the file exists 1110 | if(!file_exists($importFile)) { 1111 | // Does not exists 1112 | throw new Exception("loadXml: Import file does not exists"); 1113 | return; 1114 | } 1115 | 1116 | // Create default values 1117 | $settings = Array("linesToIgnore" => 0); 1118 | 1119 | // Check the import settings 1120 | if(gettype($importSettings) == "array") { 1121 | $settings = array_merge($settings, $importSettings); 1122 | } 1123 | 1124 | // Add the prefix to the import table 1125 | $table = self::$prefix . $importTable; 1126 | 1127 | // Add 1 more slash to every slash so maria will interpret it as a path 1128 | $importFile = str_replace("\\", "\\\\", $importFile); 1129 | 1130 | // Build SQL Syntax 1131 | $sqlSyntax = sprintf('LOAD XML INFILE \'%s\' INTO TABLE %s', 1132 | $importFile, $table); 1133 | 1134 | // FIELDS 1135 | if(isset($settings["rowTag"])) { 1136 | $sqlSyntax .= sprintf(' ROWS IDENTIFIED BY \'%s\'', $settings["rowTag"]); 1137 | } 1138 | 1139 | // IGNORE LINES 1140 | $sqlSyntax .= sprintf(' IGNORE %d LINES', $settings["linesToIgnore"]); 1141 | 1142 | // Exceute the query unprepared because LOAD XML only works with unprepared statements. 1143 | $result = $this->queryUnprepared($sqlSyntax); 1144 | 1145 | // Are there rows modified? 1146 | // Let the user know if the import failed / succeeded 1147 | return (bool) $result; 1148 | } 1149 | 1150 | /** 1151 | * This method allows you to specify multiple (method chaining optional) ORDER BY statements for SQL queries. 1152 | * 1153 | * @uses $MySqliDb->orderBy('id', 'desc')->orderBy('name', 'desc', '^[a-z]')->orderBy('name', 'desc'); 1154 | * 1155 | * @param string $orderByField The name of the database field. 1156 | * @param string $orderByDirection Order direction. 1157 | * @param mixed $customFieldsOrRegExp Array with fieldset for ORDER BY FIELD() ordering or string with regular expresion for ORDER BY REGEXP ordering 1158 | * 1159 | * @throws Exception 1160 | * @return MysqliDb 1161 | */ 1162 | public function orderBy($orderByField, $orderbyDirection = "DESC", $customFieldsOrRegExp = null) 1163 | { 1164 | $allowedDirection = Array("ASC", "DESC"); 1165 | $orderbyDirection = strtoupper(trim($orderbyDirection)); 1166 | $orderByField = preg_replace("/[^ -a-z0-9\.\(\),_`\*\'\"]+/i", '', $orderByField); 1167 | 1168 | // Add table prefix to orderByField if needed. 1169 | //FIXME: We are adding prefix only if table is enclosed into `` to distinguish aliases 1170 | // from table names 1171 | $orderByField = preg_replace('/(\`)([`a-zA-Z0-9_]*\.)/', '\1' . self::$prefix . '\2', $orderByField); 1172 | 1173 | 1174 | if (empty($orderbyDirection) || !in_array($orderbyDirection, $allowedDirection)) { 1175 | throw new Exception('Wrong order direction: ' . $orderbyDirection); 1176 | } 1177 | 1178 | if (is_array($customFieldsOrRegExp)) { 1179 | foreach ($customFieldsOrRegExp as $key => $value) { 1180 | $customFieldsOrRegExp[$key] = preg_replace("/[^-a-z0-9\.\(\),_` ]+/i", '', $value); 1181 | } 1182 | $orderByField = 'FIELD (' . $orderByField . ', "' . implode('","', $customFieldsOrRegExp) . '")'; 1183 | }elseif(is_string($customFieldsOrRegExp)){ 1184 | $orderByField = $orderByField . " REGEXP '" . $customFieldsOrRegExp . "'"; 1185 | }elseif($customFieldsOrRegExp !== null){ 1186 | throw new Exception('Wrong custom field or Regular Expression: ' . $customFieldsOrRegExp); 1187 | } 1188 | 1189 | $this->_orderBy[$orderByField] = $orderbyDirection; 1190 | return $this; 1191 | } 1192 | 1193 | /** 1194 | * This method allows you to specify multiple (method chaining optional) GROUP BY statements for SQL queries. 1195 | * 1196 | * @uses $MySqliDb->groupBy('name'); 1197 | * 1198 | * @param string $groupByField The name of the database field. 1199 | * 1200 | * @return MysqliDb 1201 | */ 1202 | public function groupBy($groupByField) 1203 | { 1204 | $groupByField = preg_replace("/[^-a-z0-9\.\(\),_\* <>=!]+/i", '', $groupByField); 1205 | 1206 | $this->_groupBy[] = $groupByField; 1207 | return $this; 1208 | } 1209 | 1210 | 1211 | /** 1212 | * This method sets the current table lock method. 1213 | * 1214 | * @author Jonas Barascu 1215 | * @param string $method The table lock method. Can be READ or WRITE. 1216 | * 1217 | * @throws Exception 1218 | * @return MysqliDb 1219 | */ 1220 | public function setLockMethod($method) 1221 | { 1222 | // Switch the uppercase string 1223 | switch(strtoupper($method)) { 1224 | // Is it READ or WRITE? 1225 | case "READ" || "WRITE": 1226 | // Succeed 1227 | $this->_tableLockMethod = $method; 1228 | break; 1229 | default: 1230 | // Else throw an exception 1231 | throw new Exception("Bad lock type: Can be either READ or WRITE"); 1232 | break; 1233 | } 1234 | return $this; 1235 | } 1236 | 1237 | /** 1238 | * Locks a table for R/W action. 1239 | * 1240 | * @author Jonas Barascu 1241 | * @param string $table The table to be locked. Can be a table or a view. 1242 | * 1243 | * @throws Exception 1244 | * @return MysqliDb if succeeeded; 1245 | */ 1246 | public function lock($table) 1247 | { 1248 | // Main Query 1249 | $this->_query = "LOCK TABLES"; 1250 | 1251 | // Is the table an array? 1252 | if(gettype($table) == "array") { 1253 | // Loop trough it and attach it to the query 1254 | foreach($table as $key => $value) { 1255 | if(gettype($value) == "string") { 1256 | if($key > 0) { 1257 | $this->_query .= ","; 1258 | } 1259 | $this->_query .= " ".self::$prefix.$value." ".$this->_tableLockMethod; 1260 | } 1261 | } 1262 | } 1263 | else{ 1264 | // Build the table prefix 1265 | $table = self::$prefix . $table; 1266 | 1267 | // Build the query 1268 | $this->_query = "LOCK TABLES ".$table." ".$this->_tableLockMethod; 1269 | } 1270 | 1271 | // Exceute the query unprepared because LOCK only works with unprepared statements. 1272 | $result = $this->queryUnprepared($this->_query); 1273 | $errno = $this->mysqli()->errno; 1274 | 1275 | // Reset the query 1276 | $this->reset(); 1277 | 1278 | // Are there rows modified? 1279 | if($result) { 1280 | // Return true 1281 | // We can't return ourself because if one table gets locked, all other ones get unlocked! 1282 | return true; 1283 | } 1284 | // Something went wrong 1285 | else { 1286 | throw new Exception("Locking of table ".$table." failed", $errno); 1287 | } 1288 | 1289 | // Return the success value 1290 | return false; 1291 | } 1292 | 1293 | /** 1294 | * Unlocks all tables in a database. 1295 | * Also commits transactions. 1296 | * 1297 | * @author Jonas Barascu 1298 | * @return MysqliDb 1299 | */ 1300 | public function unlock() 1301 | { 1302 | // Build the query 1303 | $this->_query = "UNLOCK TABLES"; 1304 | 1305 | // Exceute the query unprepared because UNLOCK and LOCK only works with unprepared statements. 1306 | $result = $this->queryUnprepared($this->_query); 1307 | $errno = $this->mysqli()->errno; 1308 | 1309 | // Reset the query 1310 | $this->reset(); 1311 | 1312 | // Are there rows modified? 1313 | if($result) { 1314 | // return self 1315 | return $this; 1316 | } 1317 | // Something went wrong 1318 | else { 1319 | throw new Exception("Unlocking of tables failed", $errno); 1320 | } 1321 | 1322 | 1323 | // Return self 1324 | return $this; 1325 | } 1326 | 1327 | 1328 | /** 1329 | * This methods returns the ID of the last inserted item 1330 | * 1331 | * @return int The last inserted item ID. 1332 | */ 1333 | public function getInsertId() 1334 | { 1335 | return $this->mysqli()->insert_id; 1336 | } 1337 | 1338 | /** 1339 | * Escape harmful characters which might affect a query. 1340 | * 1341 | * @param string $str The string to escape. 1342 | * 1343 | * @return string The escaped string. 1344 | */ 1345 | public function escape($str) 1346 | { 1347 | return $this->mysqli()->real_escape_string($str); 1348 | } 1349 | 1350 | /** 1351 | * Method to call mysqli->ping() to keep unused connections open on 1352 | * long-running scripts, or to reconnect timed out connections (if php.ini has 1353 | * global mysqli.reconnect set to true). Can't do this directly using object 1354 | * since _mysqli is protected. 1355 | * 1356 | * @return bool True if connection is up 1357 | */ 1358 | public function ping() 1359 | { 1360 | return $this->mysqli()->ping(); 1361 | } 1362 | 1363 | /** 1364 | * This method is needed for prepared statements. They require 1365 | * the data type of the field to be bound with "i" s", etc. 1366 | * This function takes the input, determines what type it is, 1367 | * and then updates the param_type. 1368 | * 1369 | * @param mixed $item Input to determine the type. 1370 | * 1371 | * @return string The joined parameter types. 1372 | */ 1373 | protected function _determineType($item) 1374 | { 1375 | switch (gettype($item)) { 1376 | case 'NULL': 1377 | case 'string': 1378 | return 's'; 1379 | break; 1380 | 1381 | case 'boolean': 1382 | case 'integer': 1383 | return 'i'; 1384 | break; 1385 | 1386 | case 'blob': 1387 | return 'b'; 1388 | break; 1389 | 1390 | case 'double': 1391 | return 'd'; 1392 | break; 1393 | } 1394 | return ''; 1395 | } 1396 | 1397 | /** 1398 | * Helper function to add variables into bind parameters array 1399 | * 1400 | * @param string Variable value 1401 | */ 1402 | protected function _bindParam($value) 1403 | { 1404 | $this->_bindParams[0] .= $this->_determineType($value); 1405 | array_push($this->_bindParams, $value); 1406 | } 1407 | 1408 | /** 1409 | * Helper function to add variables into bind parameters array in bulk 1410 | * 1411 | * @param array $values Variable with values 1412 | */ 1413 | protected function _bindParams($values) 1414 | { 1415 | foreach ($values as $value) { 1416 | $this->_bindParam($value); 1417 | } 1418 | } 1419 | 1420 | /** 1421 | * Helper function to add variables into bind parameters array and will return 1422 | * its SQL part of the query according to operator in ' $operator ?' or 1423 | * ' $operator ($subquery) ' formats 1424 | * 1425 | * @param string $operator 1426 | * @param mixed $value Variable with values 1427 | * 1428 | * @return string 1429 | */ 1430 | protected function _buildPair($operator, $value) 1431 | { 1432 | if (!is_object($value)) { 1433 | $this->_bindParam($value); 1434 | return ' ' . $operator . ' ? '; 1435 | } 1436 | 1437 | $subQuery = $value->getSubQuery(); 1438 | $this->_bindParams($subQuery['params']); 1439 | 1440 | return " " . $operator . " (" . $subQuery['query'] . ") " . $subQuery['alias']; 1441 | } 1442 | 1443 | /** 1444 | * Internal function to build and execute INSERT/REPLACE calls 1445 | * 1446 | * @param string $tableName The name of the table. 1447 | * @param array $insertData Data containing information for inserting into the DB. 1448 | * @param string $operation Type of operation (INSERT, REPLACE) 1449 | * 1450 | * @return bool Boolean indicating whether the insert query was completed succesfully. 1451 | */ 1452 | private function _buildInsert($tableName, $insertData, $operation) 1453 | { 1454 | if ($this->isSubQuery) { 1455 | return; 1456 | } 1457 | 1458 | $this->_query = $operation . " " . implode(' ', $this->_queryOptions) . " INTO " . self::$prefix . $tableName; 1459 | $stmt = $this->_buildQuery(null, $insertData); 1460 | $status = $stmt->execute(); 1461 | $this->_stmtError = $stmt->error; 1462 | $this->_stmtErrno = $stmt->errno; 1463 | $haveOnDuplicate = !empty ($this->_updateColumns); 1464 | $this->reset(); 1465 | $this->count = $stmt->affected_rows; 1466 | 1467 | if ($stmt->affected_rows < 1) { 1468 | // in case of onDuplicate() usage, if no rows were inserted 1469 | if ($status && $haveOnDuplicate) { 1470 | return true; 1471 | } 1472 | return false; 1473 | } 1474 | 1475 | if ($stmt->insert_id > 0) { 1476 | return $stmt->insert_id; 1477 | } 1478 | 1479 | return true; 1480 | } 1481 | 1482 | /** 1483 | * Abstraction method that will compile the WHERE statement, 1484 | * any passed update data, and the desired rows. 1485 | * It then builds the SQL query. 1486 | * 1487 | * @param int|array $numRows Array to define SQL limit in format Array ($offset, $count) 1488 | * or only $count 1489 | * @param array $tableData Should contain an array of data for updating the database. 1490 | * 1491 | * @return mysqli_stmt Returns the $stmt object. 1492 | */ 1493 | protected function _buildQuery($numRows = null, $tableData = null) 1494 | { 1495 | // $this->_buildJoinOld(); 1496 | $this->_buildJoin(); 1497 | $this->_buildInsertQuery($tableData); 1498 | $this->_buildCondition('WHERE', $this->_where); 1499 | $this->_buildGroupBy(); 1500 | $this->_buildCondition('HAVING', $this->_having); 1501 | $this->_buildOrderBy(); 1502 | $this->_buildLimit($numRows); 1503 | $this->_buildOnDuplicate($tableData); 1504 | 1505 | if ($this->_forUpdate) { 1506 | $this->_query .= ' FOR UPDATE'; 1507 | } 1508 | if ($this->_lockInShareMode) { 1509 | $this->_query .= ' LOCK IN SHARE MODE'; 1510 | } 1511 | 1512 | $this->_lastQuery = $this->replacePlaceHolders($this->_query, $this->_bindParams); 1513 | 1514 | if ($this->isSubQuery) { 1515 | return; 1516 | } 1517 | 1518 | // Prepare query 1519 | $stmt = $this->_prepareQuery(); 1520 | 1521 | // Bind parameters to statement if any 1522 | if (count($this->_bindParams) > 1) { 1523 | call_user_func_array(array($stmt, 'bind_param'), $this->refValues($this->_bindParams)); 1524 | } 1525 | 1526 | return $stmt; 1527 | } 1528 | 1529 | /** 1530 | * This helper method takes care of prepared statements' "bind_result method 1531 | * , when the number of variables to pass is unknown. 1532 | * 1533 | * @param mysqli_stmt $stmt Equal to the prepared statement object. 1534 | * 1535 | * @return array The results of the SQL fetch. 1536 | */ 1537 | protected function _dynamicBindResults(mysqli_stmt $stmt) 1538 | { 1539 | $parameters = array(); 1540 | $results = array(); 1541 | /** 1542 | * @see http://php.net/manual/en/mysqli-result.fetch-fields.php 1543 | */ 1544 | $mysqlLongType = 252; 1545 | $shouldStoreResult = false; 1546 | 1547 | $meta = $stmt->result_metadata(); 1548 | 1549 | // if $meta is false yet sqlstate is true, there's no sql error but the query is 1550 | // most likely an update/insert/delete which doesn't produce any results 1551 | if (!$meta && $stmt->sqlstate) 1552 | return array(); 1553 | 1554 | $row = array(); 1555 | while ($field = $meta->fetch_field()) { 1556 | if ($field->type == $mysqlLongType) { 1557 | $shouldStoreResult = true; 1558 | } 1559 | 1560 | if ($this->_nestJoin && $field->table != $this->_tableName) { 1561 | $field->table = substr($field->table, strlen(self::$prefix)); 1562 | $row[$field->table][$field->name] = null; 1563 | $parameters[] = & $row[$field->table][$field->name]; 1564 | } else { 1565 | $row[$field->name] = null; 1566 | $parameters[] = & $row[$field->name]; 1567 | } 1568 | } 1569 | 1570 | // avoid out of memory bug in php 5.2 and 5.3. Mysqli allocates lot of memory for long* 1571 | // and blob* types. So to avoid out of memory issues store_result is used 1572 | // https://github.com/joshcam/PHP-MySQLi-Database-Class/pull/119 1573 | if ($shouldStoreResult) { 1574 | $stmt->store_result(); 1575 | } 1576 | 1577 | call_user_func_array(array($stmt, 'bind_result'), $parameters); 1578 | 1579 | $this->totalCount = 0; 1580 | $this->count = 0; 1581 | 1582 | while ($stmt->fetch()) { 1583 | if ($this->returnType == 'object') { 1584 | $result = new stdClass (); 1585 | foreach ($row as $key => $val) { 1586 | if (is_array($val)) { 1587 | $result->$key = new stdClass (); 1588 | foreach ($val as $k => $v) { 1589 | $result->$key->$k = $v; 1590 | } 1591 | } else { 1592 | $result->$key = $val; 1593 | } 1594 | } 1595 | } else { 1596 | $result = array(); 1597 | foreach ($row as $key => $val) { 1598 | if (is_array($val)) { 1599 | foreach ($val as $k => $v) { 1600 | $result[$key][$k] = $v; 1601 | } 1602 | } else { 1603 | $result[$key] = $val; 1604 | } 1605 | } 1606 | } 1607 | $this->count++; 1608 | if ($this->_mapKey) { 1609 | $results[$row[$this->_mapKey]] = count($row) > 2 ? $result : end($result); 1610 | } else { 1611 | array_push($results, $result); 1612 | } 1613 | } 1614 | 1615 | if ($shouldStoreResult) { 1616 | $stmt->free_result(); 1617 | } 1618 | 1619 | $stmt->close(); 1620 | 1621 | // stored procedures sometimes can return more then 1 resultset 1622 | if ($this->mysqli()->more_results()) { 1623 | $this->mysqli()->next_result(); 1624 | } 1625 | 1626 | if (in_array('SQL_CALC_FOUND_ROWS', $this->_queryOptions)) { 1627 | $stmt = $this->mysqli()->query('SELECT FOUND_ROWS()'); 1628 | $totalCount = $stmt->fetch_row(); 1629 | $this->totalCount = $totalCount[0]; 1630 | } 1631 | 1632 | if ($this->returnType == 'json') { 1633 | return json_encode($results); 1634 | } 1635 | 1636 | return $results; 1637 | } 1638 | 1639 | /** 1640 | * Abstraction method that will build an JOIN part of the query 1641 | * 1642 | * @return void 1643 | */ 1644 | protected function _buildJoinOld() 1645 | { 1646 | if (empty($this->_join)) { 1647 | return; 1648 | } 1649 | 1650 | foreach ($this->_join as $data) { 1651 | list ($joinType, $joinTable, $joinCondition) = $data; 1652 | 1653 | if (is_object($joinTable)) { 1654 | $joinStr = $this->_buildPair("", $joinTable); 1655 | } else { 1656 | $joinStr = $joinTable; 1657 | } 1658 | 1659 | $this->_query .= " " . $joinType . " JOIN " . $joinStr . 1660 | (false !== stripos($joinCondition, 'using') ? " " : " on ") 1661 | . $joinCondition; 1662 | } 1663 | } 1664 | 1665 | /** 1666 | * Insert/Update query helper 1667 | * 1668 | * @param array $tableData 1669 | * @param array $tableColumns 1670 | * @param bool $isInsert INSERT operation flag 1671 | * 1672 | * @throws Exception 1673 | */ 1674 | public function _buildDataPairs($tableData, $tableColumns, $isInsert) 1675 | { 1676 | foreach ($tableColumns as $column) { 1677 | $value = $tableData[$column]; 1678 | 1679 | if (!$isInsert) { 1680 | if(strpos($column,'.')===false) { 1681 | $this->_query .= "`" . $column . "` = "; 1682 | } else { 1683 | $this->_query .= str_replace('.','.`',$column) . "` = "; 1684 | } 1685 | } 1686 | 1687 | // Subquery value 1688 | if ($value instanceof MysqliDb) { 1689 | $this->_query .= $this->_buildPair("", $value) . ", "; 1690 | continue; 1691 | } 1692 | 1693 | // Simple value 1694 | if (!is_array($value)) { 1695 | $this->_bindParam($value); 1696 | $this->_query .= '?, '; 1697 | continue; 1698 | } 1699 | 1700 | // Function value 1701 | $key = key($value); 1702 | $val = $value[$key]; 1703 | switch ($key) { 1704 | case '[I]': 1705 | $this->_query .= $column . $val . ", "; 1706 | break; 1707 | case '[F]': 1708 | $this->_query .= $val[0] . ", "; 1709 | if (!empty($val[1])) { 1710 | $this->_bindParams($val[1]); 1711 | } 1712 | break; 1713 | case '[N]': 1714 | if ($val == null) { 1715 | $this->_query .= "!" . $column . ", "; 1716 | } else { 1717 | $this->_query .= "!" . $val . ", "; 1718 | } 1719 | break; 1720 | default: 1721 | throw new Exception("Wrong operation"); 1722 | } 1723 | } 1724 | $this->_query = rtrim($this->_query, ', '); 1725 | } 1726 | 1727 | /** 1728 | * Helper function to add variables into the query statement 1729 | * 1730 | * @param array $tableData Variable with values 1731 | */ 1732 | protected function _buildOnDuplicate($tableData) 1733 | { 1734 | if (is_array($this->_updateColumns) && !empty($this->_updateColumns)) { 1735 | $this->_query .= " ON DUPLICATE KEY UPDATE "; 1736 | if ($this->_lastInsertId) { 1737 | $this->_query .= $this->_lastInsertId . "=LAST_INSERT_ID (" . $this->_lastInsertId . "), "; 1738 | } 1739 | 1740 | foreach ($this->_updateColumns as $key => $val) { 1741 | // skip all params without a value 1742 | if (is_numeric($key)) { 1743 | $this->_updateColumns[$val] = ''; 1744 | unset($this->_updateColumns[$key]); 1745 | } else { 1746 | $tableData[$key] = $val; 1747 | } 1748 | } 1749 | $this->_buildDataPairs($tableData, array_keys($this->_updateColumns), false); 1750 | } 1751 | } 1752 | 1753 | /** 1754 | * Abstraction method that will build an INSERT or UPDATE part of the query 1755 | * 1756 | * @param array $tableData 1757 | */ 1758 | protected function _buildInsertQuery($tableData) 1759 | { 1760 | if (!is_array($tableData)) { 1761 | return; 1762 | } 1763 | 1764 | $isInsert = preg_match('/^[INSERT|REPLACE]/', $this->_query); 1765 | $dataColumns = array_keys($tableData); 1766 | if ($isInsert) { 1767 | if (isset ($dataColumns[0])) 1768 | $this->_query .= ' (`' . implode($dataColumns, '`, `') . '`) '; 1769 | $this->_query .= ' VALUES ('; 1770 | } else { 1771 | $this->_query .= " SET "; 1772 | } 1773 | 1774 | $this->_buildDataPairs($tableData, $dataColumns, $isInsert); 1775 | 1776 | if ($isInsert) { 1777 | $this->_query .= ')'; 1778 | } 1779 | } 1780 | 1781 | /** 1782 | * Abstraction method that will build the part of the WHERE conditions 1783 | * 1784 | * @param string $operator 1785 | * @param array $conditions 1786 | */ 1787 | protected function _buildCondition($operator, &$conditions) 1788 | { 1789 | if (empty($conditions)) { 1790 | return; 1791 | } 1792 | 1793 | //Prepare the where portion of the query 1794 | $this->_query .= ' ' . $operator; 1795 | 1796 | foreach ($conditions as $cond) { 1797 | list ($concat, $varName, $operator, $val) = $cond; 1798 | $this->_query .= " " . $concat . " " . $varName; 1799 | 1800 | switch (strtolower($operator)) { 1801 | case 'not in': 1802 | case 'in': 1803 | $comparison = ' ' . $operator . ' ('; 1804 | if (is_object($val)) { 1805 | $comparison .= $this->_buildPair("", $val); 1806 | } else { 1807 | foreach ($val as $v) { 1808 | $comparison .= ' ?,'; 1809 | $this->_bindParam($v); 1810 | } 1811 | } 1812 | $this->_query .= rtrim($comparison, ',') . ' ) '; 1813 | break; 1814 | case 'not between': 1815 | case 'between': 1816 | $this->_query .= " $operator ? AND ? "; 1817 | $this->_bindParams($val); 1818 | break; 1819 | case 'not exists': 1820 | case 'exists': 1821 | $this->_query.= $operator . $this->_buildPair("", $val); 1822 | break; 1823 | default: 1824 | if (is_array($val)) { 1825 | $this->_bindParams($val); 1826 | } elseif ($val === null) { 1827 | $this->_query .= ' ' . $operator . " NULL"; 1828 | } elseif ($val != 'DBNULL' || $val == '0') { 1829 | $this->_query .= $this->_buildPair($operator, $val); 1830 | } 1831 | } 1832 | } 1833 | } 1834 | 1835 | /** 1836 | * Abstraction method that will build the GROUP BY part of the WHERE statement 1837 | * 1838 | * @return void 1839 | */ 1840 | protected function _buildGroupBy() 1841 | { 1842 | if (empty($this->_groupBy)) { 1843 | return; 1844 | } 1845 | 1846 | $this->_query .= " GROUP BY "; 1847 | 1848 | foreach ($this->_groupBy as $key => $value) { 1849 | $this->_query .= $value . ", "; 1850 | } 1851 | 1852 | $this->_query = rtrim($this->_query, ', ') . " "; 1853 | } 1854 | 1855 | /** 1856 | * Abstraction method that will build the LIMIT part of the WHERE statement 1857 | * 1858 | * @return void 1859 | */ 1860 | protected function _buildOrderBy() 1861 | { 1862 | if (empty($this->_orderBy)) { 1863 | return; 1864 | } 1865 | 1866 | $this->_query .= " ORDER BY "; 1867 | foreach ($this->_orderBy as $prop => $value) { 1868 | if (strtolower(str_replace(" ", "", $prop)) == 'rand()') { 1869 | $this->_query .= "rand(), "; 1870 | } else { 1871 | $this->_query .= $prop . " " . $value . ", "; 1872 | } 1873 | } 1874 | 1875 | $this->_query = rtrim($this->_query, ', ') . " "; 1876 | } 1877 | 1878 | /** 1879 | * Abstraction method that will build the LIMIT part of the WHERE statement 1880 | * 1881 | * @param int|array $numRows Array to define SQL limit in format Array ($offset, $count) 1882 | * or only $count 1883 | * 1884 | * @return void 1885 | */ 1886 | protected function _buildLimit($numRows) 1887 | { 1888 | if (!isset($numRows)) { 1889 | return; 1890 | } 1891 | 1892 | if (is_array($numRows)) { 1893 | $this->_query .= ' LIMIT ' . (int) $numRows[0] . ', ' . (int) $numRows[1]; 1894 | } else { 1895 | $this->_query .= ' LIMIT ' . (int) $numRows; 1896 | } 1897 | } 1898 | 1899 | /** 1900 | * Method attempts to prepare the SQL query 1901 | * and throws an error if there was a problem. 1902 | * 1903 | * @return mysqli_stmt 1904 | * @throws Exception 1905 | */ 1906 | protected function _prepareQuery() 1907 | { 1908 | $stmt = $this->mysqli()->prepare($this->_query); 1909 | 1910 | if ($stmt !== false) { 1911 | if ($this->traceEnabled) 1912 | $this->traceStartQ = microtime(true); 1913 | return $stmt; 1914 | } 1915 | 1916 | if ($this->mysqli()->errno === 2006 && $this->autoReconnect === true && $this->autoReconnectCount === 0) { 1917 | $this->connect($this->defConnectionName); 1918 | $this->autoReconnectCount++; 1919 | return $this->_prepareQuery(); 1920 | } 1921 | 1922 | $error = $this->mysqli()->error; 1923 | $query = $this->_query; 1924 | $errno = $this->mysqli()->errno; 1925 | $this->reset(); 1926 | throw new Exception(sprintf('%s query: %s', $error, $query), $errno); 1927 | } 1928 | 1929 | /** 1930 | * Referenced data array is required by mysqli since PHP 5.3+ 1931 | * 1932 | * @param array $arr 1933 | * 1934 | * @return array 1935 | */ 1936 | protected function refValues(array &$arr) 1937 | { 1938 | //Reference in the function arguments are required for HHVM to work 1939 | //https://github.com/facebook/hhvm/issues/5155 1940 | //Referenced data array is required by mysqli since PHP 5.3+ 1941 | if (strnatcmp(phpversion(), '5.3') >= 0) { 1942 | $refs = array(); 1943 | foreach ($arr as $key => $value) { 1944 | $refs[$key] = & $arr[$key]; 1945 | } 1946 | return $refs; 1947 | } 1948 | return $arr; 1949 | } 1950 | 1951 | /** 1952 | * Function to replace ? with variables from bind variable 1953 | * 1954 | * @param string $str 1955 | * @param array $vals 1956 | * 1957 | * @return string 1958 | */ 1959 | protected function replacePlaceHolders($str, $vals) 1960 | { 1961 | $i = 1; 1962 | $newStr = ""; 1963 | 1964 | if (empty($vals)) { 1965 | return $str; 1966 | } 1967 | 1968 | while ($pos = strpos($str, "?")) { 1969 | $val = $vals[$i++]; 1970 | if (is_object($val)) { 1971 | $val = '[object]'; 1972 | } 1973 | if ($val === null) { 1974 | $val = 'NULL'; 1975 | } 1976 | $newStr .= substr($str, 0, $pos) . "'" . $val . "'"; 1977 | $str = substr($str, $pos + 1); 1978 | } 1979 | $newStr .= $str; 1980 | return $newStr; 1981 | } 1982 | 1983 | /** 1984 | * Method returns last executed query 1985 | * 1986 | * @return string 1987 | */ 1988 | public function getLastQuery() 1989 | { 1990 | return $this->_lastQuery; 1991 | } 1992 | 1993 | /** 1994 | * Method returns mysql error 1995 | * 1996 | * @return string 1997 | */ 1998 | public function getLastError() 1999 | { 2000 | if (!isset($this->_mysqli[$this->defConnectionName])) { 2001 | return "mysqli is null"; 2002 | } 2003 | return trim($this->_stmtError . " " . $this->mysqli()->error); 2004 | } 2005 | 2006 | /** 2007 | * Method returns mysql error code 2008 | * @return int 2009 | */ 2010 | public function getLastErrno () { 2011 | return $this->_stmtErrno; 2012 | } 2013 | 2014 | /** 2015 | * Mostly internal method to get query and its params out of subquery object 2016 | * after get() and getAll() 2017 | * 2018 | * @return array 2019 | */ 2020 | public function getSubQuery() 2021 | { 2022 | if (!$this->isSubQuery) { 2023 | return null; 2024 | } 2025 | 2026 | array_shift($this->_bindParams); 2027 | $val = Array('query' => $this->_query, 2028 | 'params' => $this->_bindParams, 2029 | 'alias' => isset($this->connectionsSettings[$this->defConnectionName]) ? $this->connectionsSettings[$this->defConnectionName]['host'] : null 2030 | ); 2031 | $this->reset(); 2032 | return $val; 2033 | } 2034 | 2035 | /* Helper functions */ 2036 | 2037 | /** 2038 | * Method returns generated interval function as a string 2039 | * 2040 | * @param string $diff interval in the formats: 2041 | * "1", "-1d" or "- 1 day" -- For interval - 1 day 2042 | * Supported intervals [s]econd, [m]inute, [h]hour, [d]day, [M]onth, [Y]ear 2043 | * Default null; 2044 | * @param string $func Initial date 2045 | * 2046 | * @return string 2047 | * @throws Exception 2048 | */ 2049 | public function interval($diff, $func = "NOW()") 2050 | { 2051 | $types = Array("s" => "second", "m" => "minute", "h" => "hour", "d" => "day", "M" => "month", "Y" => "year"); 2052 | $incr = '+'; 2053 | $items = ''; 2054 | $type = 'd'; 2055 | 2056 | if ($diff && preg_match('/([+-]?) ?([0-9]+) ?([a-zA-Z]?)/', $diff, $matches)) { 2057 | if (!empty($matches[1])) { 2058 | $incr = $matches[1]; 2059 | } 2060 | 2061 | if (!empty($matches[2])) { 2062 | $items = $matches[2]; 2063 | } 2064 | 2065 | if (!empty($matches[3])) { 2066 | $type = $matches[3]; 2067 | } 2068 | 2069 | if (!in_array($type, array_keys($types))) { 2070 | throw new Exception("invalid interval type in '{$diff}'"); 2071 | } 2072 | 2073 | $func .= " " . $incr . " interval " . $items . " " . $types[$type] . " "; 2074 | } 2075 | return $func; 2076 | } 2077 | 2078 | /** 2079 | * Method returns generated interval function as an insert/update function 2080 | * 2081 | * @param string $diff interval in the formats: 2082 | * "1", "-1d" or "- 1 day" -- For interval - 1 day 2083 | * Supported intervals [s]econd, [m]inute, [h]hour, [d]day, [M]onth, [Y]ear 2084 | * Default null; 2085 | * @param string $func Initial date 2086 | * 2087 | * @return array 2088 | */ 2089 | public function now($diff = null, $func = "NOW()") 2090 | { 2091 | return array("[F]" => Array($this->interval($diff, $func))); 2092 | } 2093 | 2094 | /** 2095 | * Method generates incremental function call 2096 | * 2097 | * @param int $num increment by int or float. 1 by default 2098 | * 2099 | * @throws Exception 2100 | * @return array 2101 | */ 2102 | public function inc($num = 1) 2103 | { 2104 | if (!is_numeric($num)) { 2105 | throw new Exception('Argument supplied to inc must be a number'); 2106 | } 2107 | return array("[I]" => "+" . $num); 2108 | } 2109 | 2110 | /** 2111 | * Method generates decrimental function call 2112 | * 2113 | * @param int $num increment by int or float. 1 by default 2114 | * 2115 | * @return array 2116 | * @throws Exception 2117 | */ 2118 | public function dec($num = 1) 2119 | { 2120 | if (!is_numeric($num)) { 2121 | throw new Exception('Argument supplied to dec must be a number'); 2122 | } 2123 | return array("[I]" => "-" . $num); 2124 | } 2125 | 2126 | /** 2127 | * Method generates change boolean function call 2128 | * 2129 | * @param string $col column name. null by default 2130 | * 2131 | * @return array 2132 | */ 2133 | public function not($col = null) 2134 | { 2135 | return array("[N]" => (string) $col); 2136 | } 2137 | 2138 | /** 2139 | * Method generates user defined function call 2140 | * 2141 | * @param string $expr user function body 2142 | * @param array $bindParams 2143 | * 2144 | * @return array 2145 | */ 2146 | public function func($expr, $bindParams = null) 2147 | { 2148 | return array("[F]" => array($expr, $bindParams)); 2149 | } 2150 | 2151 | /** 2152 | * Method creates new mysqlidb object for a subquery generation 2153 | * 2154 | * @param string $subQueryAlias 2155 | * 2156 | * @return MysqliDb 2157 | */ 2158 | public static function subQuery($subQueryAlias = "") 2159 | { 2160 | return new self(array('host' => $subQueryAlias, 'isSubQuery' => true)); 2161 | } 2162 | 2163 | /** 2164 | * Method returns a copy of a mysqlidb subquery object 2165 | * 2166 | * @return MysqliDb new mysqlidb object 2167 | */ 2168 | public function copy() 2169 | { 2170 | $copy = unserialize(serialize($this)); 2171 | $copy->_mysqli = array(); 2172 | return $copy; 2173 | } 2174 | 2175 | /** 2176 | * Begin a transaction 2177 | * 2178 | * @uses mysqli->autocommit(false) 2179 | * @uses register_shutdown_function(array($this, "_transaction_shutdown_check")) 2180 | */ 2181 | public function startTransaction() 2182 | { 2183 | $this->mysqli()->autocommit(false); 2184 | $this->_transaction_in_progress = true; 2185 | register_shutdown_function(array($this, "_transaction_status_check")); 2186 | } 2187 | 2188 | /** 2189 | * Transaction commit 2190 | * 2191 | * @uses mysqli->commit(); 2192 | * @uses mysqli->autocommit(true); 2193 | */ 2194 | public function commit() 2195 | { 2196 | $result = $this->mysqli()->commit(); 2197 | $this->_transaction_in_progress = false; 2198 | $this->mysqli()->autocommit(true); 2199 | return $result; 2200 | } 2201 | 2202 | /** 2203 | * Transaction rollback function 2204 | * 2205 | * @uses mysqli->rollback(); 2206 | * @uses mysqli->autocommit(true); 2207 | */ 2208 | public function rollback() 2209 | { 2210 | $result = $this->mysqli()->rollback(); 2211 | $this->_transaction_in_progress = false; 2212 | $this->mysqli()->autocommit(true); 2213 | return $result; 2214 | } 2215 | 2216 | /** 2217 | * Shutdown handler to rollback uncommited operations in order to keep 2218 | * atomic operations sane. 2219 | * 2220 | * @uses mysqli->rollback(); 2221 | */ 2222 | public function _transaction_status_check() 2223 | { 2224 | if (!$this->_transaction_in_progress) { 2225 | return; 2226 | } 2227 | $this->rollback(); 2228 | } 2229 | 2230 | /** 2231 | * Query exection time tracking switch 2232 | * 2233 | * @param bool $enabled Enable execution time tracking 2234 | * @param string $stripPrefix Prefix to strip from the path in exec log 2235 | * 2236 | * @return MysqliDb 2237 | */ 2238 | public function setTrace($enabled, $stripPrefix = null) 2239 | { 2240 | $this->traceEnabled = $enabled; 2241 | $this->traceStripPrefix = $stripPrefix; 2242 | return $this; 2243 | } 2244 | 2245 | /** 2246 | * Get where and what function was called for query stored in MysqliDB->trace 2247 | * 2248 | * @return string with information 2249 | */ 2250 | private function _traceGetCaller() 2251 | { 2252 | $dd = debug_backtrace(); 2253 | $caller = next($dd); 2254 | while (isset($caller) && $caller["file"] == __FILE__) { 2255 | $caller = next($dd); 2256 | } 2257 | 2258 | return __CLASS__ . "->" . $caller["function"] . "() >> file \"" . 2259 | str_replace($this->traceStripPrefix, '', $caller["file"]) . "\" line #" . $caller["line"] . " "; 2260 | } 2261 | 2262 | /** 2263 | * Method to check if needed table is created 2264 | * 2265 | * @param array $tables Table name or an Array of table names to check 2266 | * 2267 | * @return bool True if table exists 2268 | */ 2269 | public function tableExists($tables) 2270 | { 2271 | $tables = !is_array($tables) ? Array($tables) : $tables; 2272 | $count = count($tables); 2273 | if ($count == 0) { 2274 | return false; 2275 | } 2276 | 2277 | foreach ($tables as $i => $value) 2278 | $tables[$i] = self::$prefix . $value; 2279 | $db = isset($this->connectionsSettings[$this->defConnectionName]) ? $this->connectionsSettings[$this->defConnectionName]['db'] : null; 2280 | $this->where('table_schema', $db); 2281 | $this->where('table_name', $tables, 'in'); 2282 | $this->get('information_schema.tables', $count); 2283 | return $this->count == $count; 2284 | } 2285 | 2286 | /** 2287 | * Return result as an associative array with $idField field value used as a record key 2288 | * 2289 | * Array Returns an array($k => $v) if get(.."param1, param2"), array ($k => array ($v, $v)) otherwise 2290 | * 2291 | * @param string $idField field name to use for a mapped element key 2292 | * 2293 | * @return MysqliDb 2294 | */ 2295 | public function map($idField) 2296 | { 2297 | $this->_mapKey = $idField; 2298 | return $this; 2299 | } 2300 | 2301 | /** 2302 | * Pagination wraper to get() 2303 | * 2304 | * @access public 2305 | * @param string $table The name of the database table to work with 2306 | * @param int $page Page number 2307 | * @param array|string $fields Array or coma separated list of fields to fetch 2308 | * @return array 2309 | */ 2310 | public function paginate ($table, $page, $fields = null) { 2311 | $offset = $this->pageLimit * ($page - 1); 2312 | $res = $this->withTotalCount()->get ($table, Array ($offset, $this->pageLimit), $fields); 2313 | $this->totalPages = ceil($this->totalCount / $this->pageLimit); 2314 | return $res; 2315 | } 2316 | 2317 | /** 2318 | * This method allows you to specify multiple (method chaining optional) AND WHERE statements for the join table on part of the SQL query. 2319 | * 2320 | * @uses $dbWrapper->joinWhere('user u', 'u.id', 7)->where('user u', 'u.title', 'MyTitle'); 2321 | * 2322 | * @param string $whereJoin The name of the table followed by its prefix. 2323 | * @param string $whereProp The name of the database field. 2324 | * @param mixed $whereValue The value of the database field. 2325 | * 2326 | * @return $this 2327 | */ 2328 | public function joinWhere($whereJoin, $whereProp, $whereValue = 'DBNULL', $operator = '=', $cond = 'AND') 2329 | { 2330 | $this->_joinAnd[$whereJoin][] = Array ($cond, $whereProp, $operator, $whereValue); 2331 | return $this; 2332 | } 2333 | 2334 | /** 2335 | * This method allows you to specify multiple (method chaining optional) OR WHERE statements for the join table on part of the SQL query. 2336 | * 2337 | * @uses $dbWrapper->joinWhere('user u', 'u.id', 7)->where('user u', 'u.title', 'MyTitle'); 2338 | * 2339 | * @param string $whereJoin The name of the table followed by its prefix. 2340 | * @param string $whereProp The name of the database field. 2341 | * @param mixed $whereValue The value of the database field. 2342 | * 2343 | * @return dbWrapper 2344 | */ 2345 | public function joinOrWhere($whereJoin, $whereProp, $whereValue = 'DBNULL', $operator = '=', $cond = 'AND') 2346 | { 2347 | return $this->joinWhere($whereJoin, $whereProp, $whereValue, $operator, 'OR'); 2348 | } 2349 | 2350 | /** 2351 | * Abstraction method that will build an JOIN part of the query 2352 | */ 2353 | protected function _buildJoin () { 2354 | if (empty ($this->_join)) 2355 | return; 2356 | 2357 | foreach ($this->_join as $data) { 2358 | list ($joinType, $joinTable, $joinCondition) = $data; 2359 | 2360 | if (is_object ($joinTable)) 2361 | $joinStr = $this->_buildPair ("", $joinTable); 2362 | else 2363 | $joinStr = $joinTable; 2364 | 2365 | $this->_query .= " " . $joinType. " JOIN " . $joinStr . 2366 | (false !== stripos($joinCondition, 'using') ? " " : " on ") 2367 | . $joinCondition; 2368 | 2369 | // Add join and query 2370 | if (!empty($this->_joinAnd) && isset($this->_joinAnd[$joinStr])) { 2371 | foreach($this->_joinAnd[$joinStr] as $join_and_cond) { 2372 | list ($concat, $varName, $operator, $val) = $join_and_cond; 2373 | $this->_query .= " " . $concat ." " . $varName; 2374 | $this->conditionToSql($operator, $val); 2375 | } 2376 | } 2377 | } 2378 | } 2379 | 2380 | /** 2381 | * Convert a condition and value into the sql string 2382 | * @param String $operator The where constraint operator 2383 | * @param String $val The where constraint value 2384 | */ 2385 | private function conditionToSql($operator, $val) { 2386 | switch (strtolower ($operator)) { 2387 | case 'not in': 2388 | case 'in': 2389 | $comparison = ' ' . $operator. ' ('; 2390 | if (is_object ($val)) { 2391 | $comparison .= $this->_buildPair ("", $val); 2392 | } else { 2393 | foreach ($val as $v) { 2394 | $comparison .= ' ?,'; 2395 | $this->_bindParam ($v); 2396 | } 2397 | } 2398 | $this->_query .= rtrim($comparison, ',').' ) '; 2399 | break; 2400 | case 'not between': 2401 | case 'between': 2402 | $this->_query .= " $operator ? AND ? "; 2403 | $this->_bindParams ($val); 2404 | break; 2405 | case 'not exists': 2406 | case 'exists': 2407 | $this->_query.= $operator . $this->_buildPair ("", $val); 2408 | break; 2409 | default: 2410 | if (is_array ($val)) 2411 | $this->_bindParams ($val); 2412 | else if ($val === null) 2413 | $this->_query .= $operator . " NULL"; 2414 | else if ($val != 'DBNULL' || $val == '0') 2415 | $this->_query .= $this->_buildPair ($operator, $val); 2416 | } 2417 | } 2418 | } 2419 | 2420 | // END class 2421 | --------------------------------------------------------------------------------