1 <?php
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
70
71 class GxDBConnect {
72
73 public $conn;
74
75
76 public $col_whitelist;
77
78
79 public $tbl_whitelist;
80
81
82 public $get_last_stmt;
83
84
85 public static $version = "3.0.0";
86
87
88 public static $echo_uncaught_errors = false;
89
90 private $blacklist = array("DROP", "DELETE", "--", "/*", "xp_", ";");
91
92 93 94 95 96 97 98 99 100
101 public function __construct($dsn, $usr = "root", $pw = "root", array $opts = null) {
102 set_exception_handler(array($this, "on_uncaught_exception"));
103 set_error_handler(array($this, "on_uncaught_error"));
104
105 try {
106 $this->conn = new PDO($dsn, $usr, $pw, $opts);
107 } catch (PDOException $e) {
108 throw new GxDBException($e->getMessage(), 1);
109 }
110
111 $this->conn->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
112 $this->conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
113 }
114
115 116 117
118 public static function on_uncaught_exception($e) {
119 if (self::$echo_uncaught_errors) {
120 exit("[Uncaught GxDBConnect Error]: " . $e->getMessage());
121 } else {
122 exit("[Uncaught GxDBConnect Error]: To see uncaught errors, set 'GxDBConnect::\$echo_uncaught_errors' to true.");
123 }
124 }
125
126 127 128
129 public static function on_uncaught_error($err_num, $err_msg) {
130 if (self::$echo_uncaught_errors) {
131 exit("[Uncaught GxDBConnect Error]: " . $err_msg);
132 } else {
133 exit("[Uncaught GxDBConnect Error]: To see uncaught errors, set 'GxDBConnect::\$echo_uncaught_errors' to true.");
134 }
135 }
136
137 private function blacklist_check($s) {
138 $regex = "";
139
140 if (isset($this->blacklist) && count($this->blacklist) > 0) {
141 for ($i = 0; $i < count($this->blacklist); $i++) {
142 if ($i > 0) {
143 $regex .= "|";
144 }
145 $regex .= preg_quote($this->blacklist[$i]);
146 }
147
148 $regex = str_replace("/", "\/", $regex);
149
150
151 $s = preg_replace("/\s+/", " ", $s);
152
153
154 $s = preg_replace("/\".*?\"|'.*?'/", "", $s);
155
156 if (preg_match("/$regex/i", $s)) {
157 throw new GxDBException("Your query statement contains a blacklisted value. See blacklist_list, index " . ($i - 1) . ".", 2);
158 }
159 }
160 }
161
162 163 164 165 166 167 168
169 public function blacklist_add($str) {
170 $str = (string) $str;
171 $at = array_search($str, $this->blacklist);
172
173 if(!$at){
174 array_push($this->blacklist, $str);
175 }
176 }
177
178 179 180 181
182 public function blacklist_remove($str) {
183 $at = array_search(strtolower((string) $str), array_map("strtolower", $this->blacklist));
184
185 if($at !== false){
186 array_splice($this->blacklist, $at, 1);
187 }
188 }
189
190 191 192 193
194 public function blacklist_list() {
195 return $this->blacklist;
196 }
197
198 199 200 201
202 public function select_db($db) {
203 $this->conn->exec("USE $db");
204 }
205
206 207 208 209 210 211
212 public function col_check($col) {
213 if (isset($this->col_whitelist)) {
214 if (!(array_search($col, $this->col_whitelist, true) !== false)) {
215 throw new GxDBException("You do not have permission to query one of the columns in your statement.", 3);
216 }
217 }
218
219 return $col;
220 }
221
222 223 224 225 226 227
228 public function tbl_check($tbl) {
229 if (isset($this->tbl_whitelist)) {
230 if (!(array_search($tbl, $this->tbl_whitelist, true) !== false)) {
231 throw new GxDBException("You do not have permission to query one of the tables in your statement.", 4);
232 }
233 }
234
235 return $tbl;
236 }
237
238 239 240 241 242 243 244
245 public function bind_value($parameter, $value, $data_type = null) {
246 return array(
247 $parameter,
248 $value,
249 $data_type
250 );
251 }
252
253 254 255 256 257 258 259 260
261 public function query($stmt, array $bind = null, $fetch_how = PDO::FETCH_ASSOC) {
262 $this->blacklist_check($stmt);
263
264 try {
265 $q = $this->conn->prepare($stmt . ";");
266 $this->get_last_stmt = $q->queryString;
267
268 if (isset($bind)) {
269 for ($i = 0; $i < count($bind); $i++) {
270 if ($bind[$i][2]) {
271 $q->bindValue($bind[$i][0], $bind[$i][1], $bind[$i][2]);
272 } else {
273 $q->bindValue($bind[$i][0], $bind[$i][1]);
274 }
275 }
276 }
277
278 $q->closeCursor();
279 $q->execute();
280
281 return $q->fetchAll($fetch_how);
282 } catch (PDOException $e) {
283 throw new GxDBException($e->getMessage(), 5);
284 }
285 }
286
287 288 289
290 public function close() {
291 $this->conn = null;
292 }
293
294 295 296 297 298
299 public function run_tbl_exists($tbl) {
300 try {
301 $q = $this->conn->prepare("SELECT 1 FROM {$this->tbl_check($tbl)} LIMIT 1;");
302 $this->get_last_stmt = $q->queryString;
303 $q->closeCursor();
304 $q->execute();
305
306 return true;
307 } catch (PDOException $e) {
308 return false;
309 }
310 }
311
312 313 314 315 316
317 public function run_col_count($tbl) {
318 $q = $this->conn->prepare("SELECT * FROM {$this->tbl_check($tbl)} LIMIT 1;");
319 $this->get_last_stmt = $q->queryString;
320
321 $q->closeCursor();
322 $q->execute();
323
324 return $q->columnCount();
325 }
326
327 328 329 330 331
332 public function run_col_info($tbl) {
333 $q = $this->conn->prepare("SHOW COLUMNS FROM {$this->tbl_check($tbl)};");
334 $this->get_last_stmt = $q->queryString;
335
336 $q->closeCursor();
337 $q->execute();
338
339 return $q->fetchAll(PDO::FETCH_ASSOC);
340 }
341
342 343 344 345 346 347
348 public function run_col_data($col, $tbl) {
349 $q = $this->conn->prepare("SELECT {$this->col_check($col)} FROM {$this->tbl_check($tbl)};");
350 $this->get_last_stmt = $q->queryString;
351
352 $q->closeCursor();
353 $q->execute();
354
355 $fin = array();
356 foreach ($q as $r) {
357 array_push($fin, $r[0]);
358 }
359
360 return $fin;
361 }
362
363 364 365 366 367 368
369 public function run_col_exists($col, $tbl) {
370 try {
371 $q = $this->conn->prepare("SELECT {$this->col_check($col)} FROM {$this->tbl_check($tbl)} LIMIT 1;");
372 $this->get_last_stmt = $q->queryString;
373 $q->closeCursor();
374 $q->execute();
375
376 return true;
377 } catch (PDOException $e) {
378 return false;
379 }
380 }
381
382 383 384 385 386
387 public function run_row_total($tbl) {
388 $q = $this->conn->prepare("SELECT COUNT(*) FROM {$this->tbl_check($tbl)};");
389 $this->get_last_stmt = $q->queryString;
390
391 $q->closeCursor();
392 $q->execute();
393
394 return $q->fetchColumn();
395 }
396
397 398 399 400 401 402
403 public function run_row_data($row, $tbl) {
404 $row = (int) $row;
405 $q = $this->conn->prepare("SELECT * FROM {$this->tbl_check($tbl)};");
406 $this->get_last_stmt = $q->queryString;
407
408 $q->closeCursor();
409 $q->execute();
410
411 if ($row > $this->run_row_total($tbl)) {
412 return null;
413 } else {
414 $arr = $q->fetchAll(PDO::FETCH_NUM);
415
416 return $arr[$row];
417 }
418 }
419
420 421 422 423 424 425 426
427 public function run_export($tbl, $pretty_print = false, $relative_dir = "") {
428 $file_name = date("m-d-y") . "-" . $tbl . ".json";
429
430 $obj["GxDBConnect"] = self::$version;
431 $obj["table"] = $tbl;
432 $obj["colCount"] = $this->run_col_count($tbl);
433 $obj["rowCount"] = $this->run_row_total($tbl);
434
435
436 $desc = $this->query("DESCRIBE $tbl");
437 $keys = array_keys($desc[0]);
438
439 for ($i = 0, $len = count($desc); $i < $len; $i++) {
440 for ($k = 0, $lenK = count($keys); $k < $lenK; $k++) {
441 $cols[$i][$keys[$k]] = isset($desc[$i][$keys[$k]]) ? utf8_encode($desc[$i][$keys[$k]]) : "";
442 }
443 }
444
445 $obj["cols"] = $cols;
446
447
448 $q = $this->conn->prepare("SELECT * FROM $tbl;");
449 $q->closeCursor();
450 $q->execute();
451
452 $obj["rows"] = $q->fetchAll(PDO::FETCH_NUM);
453
454
455 $json = json_encode($obj, ($pretty_print && defined(JSON_PRETTY_PRINT) ? JSON_PRETTY_PRINT : 0));
456
457 if (json_last_error() != JSON_ERROR_NONE) {
458 die("There was a problem creating your JSON export: " . json_last_error_msg());
459 }
460
461 if ($relative_dir) {
462 $relative_dir = trim($relative_dir, "/") . "/";
463
464 if (!is_dir($relative_dir)) {
465 mkdir($relative_dir);
466 }
467 }
468
469 return file_put_contents($relative_dir . $file_name, $json) > 0;
470 }
471
472 473 474 475 476 477 478 479
480 public function run_tbl_to_html($stmt, $paginate_at = 0, $pg_query_name = "pg", $use_default_styles = true) {
481 $this->blacklist_check($stmt);
482
483 $bg_color = "#CCC";
484 $col_color_odd = "#F9F9F9";
485 $col_color_even = "#F0F0F0";
486 $header_color = "#9C9C9C";
487 $alt_row = true;
488 $row_num = 1;
489
490
491 $qNames = $this->conn->prepare($stmt . ";");
492 $qNames->closeCursor();
493 $qNames->execute();
494
495 $names = $qNames->fetchAll(PDO::FETCH_ASSOC);
496 $col_names = array_keys($names[0]);
497
498
499 if ($paginate_at) {
500 if (stristr($stmt, "LIMIT") || stristr($stmt, "OFFSET")) {
501 throw new GxDBException("Your 'run_tbl_to_html' statement cannot have a LIMIT or OFFSET clause.", 6);
502 }
503
504 $totalPgs = ceil($qNames->rowCount() / $paginate_at);
505 $pgQuery = isset($_GET[$pg_query_name]) ? $_GET[$pg_query_name] : 1;
506
507 if ($pgQuery > $totalPgs) {
508 $pgQuery = $totalPgs;
509 }
510
511 $stmt .= " LIMIT $paginate_at OFFSET " . $paginate_at * ($pgQuery - 1);
512 }
513
514 $q = $this->conn->prepare($stmt . ";");
515 $q->closeCursor();
516 $q->execute();
517
518 $len = $q->columnCount();
519
520 if ($use_default_styles) {
521 $styles = " style=\"width:100%; background-color:$bg_color; text-align:center\"";
522 $styles2 = " style=\"padding:3px 12px; background-color:$header_color\"";
523 }
524
525 $echo = "<table class=\"GxDBConnectTable\"$styles>\n<tr class=\"headerRow\"$styles2>\n";
526
527
528 for ($i = 0; $i < $len; $i++) {
529 $col_num = $i + 1;
530
531 $echo .= "<td class=\"col$col_num\">$col_names[$i]</td>\n";
532 }
533
534 $echo .= "</tr>\n";
535
536
537 foreach ($q as $row) {
538 for ($i = 0; $i < $len; $i++) {
539 if ($i == 0) {
540 if ($alt_row) {
541 $alt_row = false;
542 $colColor = $col_color_odd;
543 $alt_row_class = "oddRow";
544 } else {
545 $alt_row = true;
546 $colColor = $col_color_even;
547 $alt_row_class = "evenRow";
548 }
549 }
550
551 if ($use_default_styles) {
552 $styles = " style=\"padding:3px 12px; background-color:$colColor;\"";
553 }
554
555 if (($i % $len) == 0) {
556 $echo .= "<tr class=\"row$row_num $alt_row_class\"$styles>\n";
557 }
558
559 $col_num = $i + 1;
560
561 $echo .= "<td class=\"col$col_num\">$row[$i]</td>\n";
562
563 if ($i == ($len - 1)) {
564 $echo .= "</tr>\n";
565 $row_num++;
566 }
567 }
568 }
569
570 $echo .= "</table>";
571
572 echo $echo;
573
574 if ($paginate_at) {
575 if ($totalPgs > 1) {
576 $this->paginate($pgQuery, $pg_query_name, $totalPgs);
577 }
578 }
579 }
580
581 private function paginate($pg, $pg_query_name, $totalPgs) {
582 $echo = "\n<p class=\"GxDBConnectPagination\">\n" . ($pg > 1 ? "<a href=\"" . $this->updateQueryStr($pg_query_name, $pg - 1) . "\"><</a> " : "< ") . "\n";
583
584 for ($i = 1; $i <= $totalPgs; $i++) {
585 $echo .= ($pg == $i ? $i . " " : "<a href=\"" . $this->updateQueryStr($pg_query_name, $i) . "\">$i</a>") . "\n";
586 }
587
588 $echo .= ($pg < $totalPgs ? "<a href=\"" . $this->updateQueryStr($pg_query_name, $pg + 1) . "\">></a> " : ">") . "\n</p>\n";
589
590 echo $echo;
591 }
592
593 private function updateQueryStr($pg_query_name, $pg) {
594 if (!strstr($_SERVER['QUERY_STRING'], $pg_query_name . "=")) {
595 if (strpos($_SERVER['REQUEST_URI'], "?")) {
596 $div = "&";
597 } else {
598 $div = "?";
599 }
600
601 return $_SERVER['REQUEST_URI'] . $div . "$pg_query_name=$pg";
602 } else {
603 return preg_replace("/" . preg_quote($pg_query_name) . "=\d*/", $pg_query_name . "=" . $pg, $_SERVER['REQUEST_URI']);
604 }
605 }
606
607 }
608
609
610
611 612 613
614 class GxDBException extends Exception {
615 616 617 618 619
620 public function __construct($message, $code) {
621 parent::__construct($message, $code);
622 }
623
624 625 626 627
628 public function __toString() {
629 return __CLASS__ . ": [{$this->code}]: {$this->message}";
630 }
631
632 }
633
634 ?>
635