Browse Source

Various refactoring

Timothy J. Warren 8 months ago
parent
commit
91eb8123d1

+ 4
- 5
README.md View File

@@ -16,7 +16,6 @@ A query builder/database abstraction layer, using prepared statements for securi
16 16
 
17 17
 ## Databases Supported
18 18
 
19
-* Firebird (via interbase extension)
20 19
 * MySQL
21 20
 * PostgreSQL
22 21
 * SQLite
@@ -41,7 +40,7 @@ $params = array(
41 40
 	'database' => 'test_db',
42 41
 
43 42
 	// Only required for
44
-	// SQLite or Firebird
43
+	// SQLite 
45 44
 	'file' => '/path/to/db/file',
46 45
 
47 46
 	// Optional paramaters
@@ -85,7 +84,7 @@ Underscored methods are also aliased to camel case methods.
85 84
 #### You can also run queries manually.
86 85
 
87 86
 To run a prepared statement, call
88
-`$db->prepare_execute($sql, $params)`.
87
+`$db->prepareExecute($sql, $params)`.
89 88
 
90 89
 To run a plain query, `$db->query($sql)`
91 90
 
@@ -98,8 +97,8 @@ An example of a moderately complex query:
98 97
 $query = $db->select('id, key as k, val')
99 98
 	->from('table t')
100 99
 	->where('k >', 3)
101
-	->or_where('id !=' 5)
102
-	->order_by('val', 'DESC')
100
+	->orWhere('id !=' 5)
101
+	->orderBy('val', 'DESC')
103 102
 	->limit(3, 1)
104 103
 	->get();
105 104
 ```

+ 0
- 8
build/phpunit.xml View File

@@ -24,14 +24,6 @@
24 24
 		<testsuite name="SQLite Tests">
25 25
 			<directory>./../tests/Drivers/SQLite/</directory>
26 26
 		</testsuite>
27
-		<!-- <testsuite name="FirebirdTests">
28
-			<file>../tests/databases/firebird/FirebirdTest.php</file>
29
-			<file>../tests/databases/firebird/FirebirdQBTest.php</file>
30
-		</testsuite>
31
-		<testsuite name="OCITests">
32
-			<file>../tests/databases/oci/OCITest.php</file>
33
-			<file>../tests/databases/oci/OCIQBTest.php</file>
34
-		</testsuite> -->
35 27
 	</testsuites>
36 28
 	<logging>
37 29
 		<log type="coverage-html" target="./../coverage"/>

+ 0
- 6
phpstan.neon View File

@@ -1,12 +1,6 @@
1 1
 parameters:
2 2
 	autoload_files:
3 3
 		- %rootDir%/../../../tests/bootstrap.php
4
-		- %rootDir%/../../../tests/databases/mysql/MySQLTest.php
5
-		- %rootDir%/../../../tests/databases/mysql/MySQLQBTest.php
6
-		- %rootDir%/../../../tests/databases/pgsql/PgSQLTest.php
7
-		- %rootDir%/../../../tests/databases/pgsql/PgSQLQBTest.php
8
-		- %rootDir%/../../../tests/databases/sqlite/SQLiteTest.php
9
-		- %rootDir%/../../../tests/databases/sqlite/SQLiteQBTest.php
10 4
 	ignoreErrors:
11 5
 		- '#Access to an undefined property Aviat\\\Ion\\\Friend::\$[a-zA-Z0-9_]+#'
12 6
 		- '#Call to an undefined method Aviat\\\Ion\\\Friend::[a-zA-Z0-9_]+\(\)#'

+ 0
- 35
phpunit.xml View File

@@ -1,35 +0,0 @@
1
-<?xml version="1.0" encoding="UTF-8"?>
2
-<phpunit
3
-        colors="true"
4
-        stopOnFailure="false"
5
-        bootstrap="tests/bootstrap.php">
6
-    <filter>
7
-        <whitelist>
8
-            <directory suffix=".php">src/</directory>
9
-            <file>autoload.php</file>
10
-        </whitelist>
11
-    </filter>
12
-    <testsuites>
13
-        <testsuite name="CoreTests">
14
-            <file>tests/core/core_test.php</file>
15
-            <file>tests/core/query_parser_test.php</file>
16
-            <file>tests/core/connection_manager_test.php</file>
17
-        </testsuite>
18
-        <testsuite name="MySQLTests">
19
-            <file>tests/databases/mysql/MySQLTest.php</file>
20
-            <file>tests/databases/mysql/MySQLQBTest.php</file>
21
-        </testsuite>
22
-        <testsuite name="PgSQLTests">
23
-            <file>tests/databases/pgsql/PgSQLTest.php</file>
24
-            <file>tests/databases/pgsql/PgSQLQBTest.php</file>
25
-        </testsuite>
26
-        <testsuite name="SQLiteTests">
27
-            <file>tests/databases/sqlite/SQLiteTest.php</file>
28
-            <file>tests/databases/sqlite/SQLiteQBTest.php</file>
29
-        </testsuite>
30
-        <testsuite name="FirebirdTests">
31
-            <file>tests/databases/firebird/FirebirdTest.php</file>
32
-            <file>tests/databases/firebird/FirebirdQBTest.php</file>
33
-        </testsuite>
34
-    </testsuites>
35
-</phpunit>

+ 39
- 40
src/Drivers/AbstractDriver.php View File

@@ -98,10 +98,10 @@ abstract class AbstractDriver extends PDO implements DriverInterface {
98 98
 	 *
99 99
 	 * @return void
100 100
 	 */
101
-	protected function _loadSubClasses()
101
+	protected function _loadSubClasses(): void
102 102
 	{
103 103
 		// Load the sql and util class for the driver
104
-		$thisClass = get_class($this);
104
+		$thisClass = \get_class($this);
105 105
 		$nsArray = explode("\\", $thisClass);
106 106
 		array_pop($nsArray);
107 107
 		$driver = array_pop($nsArray);
@@ -124,11 +124,11 @@ abstract class AbstractDriver extends PDO implements DriverInterface {
124 124
 	{
125 125
 		if (
126 126
 			isset($this->$name)
127
-			&& is_object($this->$name)
127
+			&& \is_object($this->$name)
128 128
 			&& method_exists($this->$name, '__invoke')
129 129
 		)
130 130
 		{
131
-			return call_user_func_array([$this->$name, '__invoke'], $args);
131
+			return \call_user_func_array([$this->$name, '__invoke'], $args);
132 132
 		}
133 133
 	}
134 134
 
@@ -152,7 +152,7 @@ abstract class AbstractDriver extends PDO implements DriverInterface {
152 152
 	 * @param string $queryString
153 153
 	 * @return void
154 154
 	 */
155
-	public function setLastQuery(string $queryString)
155
+	public function setLastQuery(string $queryString): void
156 156
 	{
157 157
 		$this->lastQuery = $queryString;
158 158
 	}
@@ -205,9 +205,9 @@ abstract class AbstractDriver extends PDO implements DriverInterface {
205 205
 		// Prepare the sql, save the statement for easy access later
206 206
 		$this->statement = $this->prepare($sql);
207 207
 
208
-		if( ! (is_array($data) || is_object($data)))
208
+		if( ! (\is_array($data) || \is_object($data)))
209 209
 		{
210
-			throw new InvalidArgumentException("Data argument must be an object or associative array");
210
+			throw new InvalidArgumentException('Data argument must be an object or associative array');
211 211
 		}
212 212
 
213 213
 		// Bind the parameters
@@ -232,7 +232,7 @@ abstract class AbstractDriver extends PDO implements DriverInterface {
232 232
 	 * @param array $params
233 233
 	 * @return PDOStatement
234 234
 	 */
235
-	public function prepareExecute($sql, $params)
235
+	public function prepareExecute($sql, $params): PDOStatement
236 236
 	{
237 237
 		$this->statement = $this->prepareQuery($sql, $params);
238 238
 		$this->statement->execute();
@@ -245,7 +245,7 @@ abstract class AbstractDriver extends PDO implements DriverInterface {
245 245
 	 *
246 246
 	 * @return int
247 247
 	 */
248
-	public function affectedRows()
248
+	public function affectedRows(): int
249 249
 	{
250 250
 		// Return number of rows affected
251 251
 		return $this->statement->rowCount();
@@ -256,7 +256,7 @@ abstract class AbstractDriver extends PDO implements DriverInterface {
256 256
 	 * @param string $table
257 257
 	 * @return string
258 258
 	 */
259
-	public function prefixTable($table)
259
+	public function prefixTable($table): string
260 260
 	{
261 261
 		// Add the prefix to the table name
262 262
 		// before quoting it
@@ -286,7 +286,7 @@ abstract class AbstractDriver extends PDO implements DriverInterface {
286 286
 	 * @param string $table
287 287
 	 * @return string
288 288
 	 */
289
-	public function quoteTable($table)
289
+	public function quoteTable($table): string
290 290
 	{
291 291
 		$table = $this->prefixTable($table);
292 292
 
@@ -298,11 +298,11 @@ abstract class AbstractDriver extends PDO implements DriverInterface {
298 298
 	 * Surrounds the string with the databases identifier escape characters
299 299
 	 *
300 300
 	 * @param mixed $identifier
301
-	 * @return string
301
+	 * @return string|array
302 302
 	 */
303 303
 	public function quoteIdent($identifier)
304 304
 	{
305
-		if (is_array($identifier))
305
+		if (\is_array($identifier))
306 306
 		{
307 307
 			return array_map([$this, __METHOD__], $identifier);
308 308
 		}
@@ -335,7 +335,6 @@ abstract class AbstractDriver extends PDO implements DriverInterface {
335 335
 		}
336 336
 
337 337
 		return $raw;
338
-
339 338
 	}
340 339
 
341 340
 	/**
@@ -343,7 +342,7 @@ abstract class AbstractDriver extends PDO implements DriverInterface {
343 342
 	 *
344 343
 	 * @return array
345 344
 	 */
346
-	public function getSchemas()
345
+	public function getSchemas(): ?array
347 346
 	{
348 347
 		return NULL;
349 348
 	}
@@ -353,7 +352,7 @@ abstract class AbstractDriver extends PDO implements DriverInterface {
353 352
 	 *
354 353
 	 * @return array
355 354
 	 */
356
-	public function getTables()
355
+	public function getTables(): ?array
357 356
 	{
358 357
 		$tables = $this->driverQuery('tableList');
359 358
 		natsort($tables);
@@ -365,7 +364,7 @@ abstract class AbstractDriver extends PDO implements DriverInterface {
365 364
 	 *
366 365
 	 * @return array
367 366
 	 */
368
-	public function getDbs()
367
+	public function getDbs(): array
369 368
 	{
370 369
 		return $this->driverQuery('dbList');
371 370
 	}
@@ -375,7 +374,7 @@ abstract class AbstractDriver extends PDO implements DriverInterface {
375 374
 	 *
376 375
 	 * @return array
377 376
 	 */
378
-	public function getViews()
377
+	public function getViews(): ?array
379 378
 	{
380 379
 		$views = $this->driverQuery('viewList');
381 380
 		sort($views);
@@ -387,7 +386,7 @@ abstract class AbstractDriver extends PDO implements DriverInterface {
387 386
 	 *
388 387
 	 * @return array
389 388
 	 */
390
-	public function getSequences()
389
+	public function getSequences(): ?array
391 390
 	{
392 391
 		return $this->driverQuery('sequenceList');
393 392
 	}
@@ -397,7 +396,7 @@ abstract class AbstractDriver extends PDO implements DriverInterface {
397 396
 	 *
398 397
 	 * @return array
399 398
 	 */
400
-	public function getFunctions()
399
+	public function getFunctions(): ?array
401 400
 	{
402 401
 		return $this->driverQuery('functionList', FALSE);
403 402
 	}
@@ -407,7 +406,7 @@ abstract class AbstractDriver extends PDO implements DriverInterface {
407 406
 	 *
408 407
 	 * @return array
409 408
 	 */
410
-	public function getProcedures()
409
+	public function getProcedures(): ?array
411 410
 	{
412 411
 		return $this->driverQuery('procedureList', FALSE);
413 412
 	}
@@ -417,7 +416,7 @@ abstract class AbstractDriver extends PDO implements DriverInterface {
417 416
 	 *
418 417
 	 * @return array
419 418
 	 */
420
-	public function getTriggers()
419
+	public function getTriggers(): ?array
421 420
 	{
422 421
 		return $this->driverQuery('triggerList', FALSE);
423 422
 	}
@@ -428,7 +427,7 @@ abstract class AbstractDriver extends PDO implements DriverInterface {
428 427
 	 *
429 428
 	 * @return array
430 429
 	 */
431
-	public function getSystemTables()
430
+	public function getSystemTables(): ?array
432 431
 	{
433 432
 		return $this->driverQuery('systemTableList');
434 433
 	}
@@ -439,7 +438,7 @@ abstract class AbstractDriver extends PDO implements DriverInterface {
439 438
 	 * @param string $table
440 439
 	 * @return array
441 440
 	 */
442
-	public function getColumns($table)
441
+	public function getColumns($table): ?array
443 442
 	{
444 443
 		return $this->driverQuery($this->getSql()->columnList($this->prefixTable($table)), FALSE);
445 444
 	}
@@ -450,7 +449,7 @@ abstract class AbstractDriver extends PDO implements DriverInterface {
450 449
 	 * @param string $table
451 450
 	 * @return array
452 451
 	 */
453
-	public function getFks($table)
452
+	public function getFks($table): ?array
454 453
 	{
455 454
 		return $this->driverQuery($this->getSql()->fkList($table), FALSE);
456 455
 	}
@@ -461,7 +460,7 @@ abstract class AbstractDriver extends PDO implements DriverInterface {
461 460
 	 * @param string $table
462 461
 	 * @return array
463 462
 	 */
464
-	public function getIndexes($table)
463
+	public function getIndexes($table): ?array
465 464
 	{
466 465
 		return $this->driverQuery($this->getSql()->indexList($this->prefixTable($table)), FALSE);
467 466
 	}
@@ -471,7 +470,7 @@ abstract class AbstractDriver extends PDO implements DriverInterface {
471 470
 	 *
472 471
 	 * @return array
473 472
 	 */
474
-	public function getTypes()
473
+	public function getTypes(): ?array
475 474
 	{
476 475
 		return $this->driverQuery('typeList', FALSE);
477 476
 	}
@@ -481,19 +480,19 @@ abstract class AbstractDriver extends PDO implements DriverInterface {
481 480
 	 *
482 481
 	 * @param string|array|null $query
483 482
 	 * @param bool $filteredIndex
484
-	 * @return array
483
+	 * @return array|null
485 484
 	 */
486
-	public function driverQuery($query, $filteredIndex=TRUE)
485
+	public function driverQuery($query, $filteredIndex=TRUE): ?array
487 486
 	{
488 487
 		// Call the appropriate method, if it exists
489
-		if (is_string($query) && method_exists($this->sql, $query))
488
+		if (\is_string($query) && method_exists($this->sql, $query))
490 489
 		{
491 490
 			$query = $this->getSql()->$query();
492 491
 		}
493 492
 
494 493
 		// Return if the values are returned instead of a query,
495 494
 		// or if the query doesn't apply to the driver
496
-		if ( ! is_string($query))
495
+		if ( ! \is_string($query))
497 496
 		{
498 497
 			return $query;
499 498
 		}
@@ -501,10 +500,10 @@ abstract class AbstractDriver extends PDO implements DriverInterface {
501 500
 		// Run the query!
502 501
 		$res = $this->query($query);
503 502
 
504
-		$flag = ($filteredIndex) ? PDO::FETCH_NUM : PDO::FETCH_ASSOC;
503
+		$flag = $filteredIndex ? PDO::FETCH_NUM : PDO::FETCH_ASSOC;
505 504
 		$all = $res->fetchAll($flag);
506 505
 
507
-		return ($filteredIndex) ? \db_filter($all, 0) : $all;
506
+		return $filteredIndex ? \db_filter($all, 0) : $all;
508 507
 	}
509 508
 
510 509
 	/**
@@ -513,7 +512,7 @@ abstract class AbstractDriver extends PDO implements DriverInterface {
513 512
 	 * @see http://us3.php.net/manual/en/pdostatement.rowcount.php#87110
514 513
 	 * @return int|null
515 514
 	 */
516
-	public function numRows()
515
+	public function numRows(): ?int
517 516
 	{
518 517
 		$regex = '/^SELECT\s+(?:ALL\s+|DISTINCT\s+)?(?:.*?)\s+FROM\s+(.*)$/i';
519 518
 		$output = [];
@@ -537,7 +536,7 @@ abstract class AbstractDriver extends PDO implements DriverInterface {
537 536
 	public function insertBatch($table, $data=[])
538 537
 	{
539 538
 		$data = (array) $data;
540
-		$firstRow = (array) current($data);
539
+		$firstRow = current($data);
541 540
 		if (is_scalar($firstRow))
542 541
 		{
543 542
 			return NULL;
@@ -558,7 +557,7 @@ abstract class AbstractDriver extends PDO implements DriverInterface {
558 557
 
559 558
 		// Create the placeholder groups
560 559
 		$params = array_fill(0, count($fields), '?');
561
-		$paramString = "(" . implode(',', $params) . ")";
560
+		$paramString = '(' . implode(',', $params) . ')';
562 561
 		$paramList = array_fill(0, count($data), $paramString);
563 562
 
564 563
 		// Append the placeholder groups to the query
@@ -594,7 +593,7 @@ abstract class AbstractDriver extends PDO implements DriverInterface {
594 593
 		// and is not already quoted before quoting
595 594
 		// that value, otherwise, return the original value
596 595
 		return (
597
-			is_string($str)
596
+			\is_string($str)
598 597
 			&& strpos($str, $this->escapeCharOpen) !== 0
599 598
 			&& strrpos($str, $this->escapeCharClose) !== 0
600 599
 		)
@@ -609,7 +608,7 @@ abstract class AbstractDriver extends PDO implements DriverInterface {
609 608
 	 * @param string $str
610 609
 	 * @return string
611 610
 	 */
612
-	protected function _prefix($str)
611
+	protected function _prefix($str): string
613 612
 	{
614 613
 		// Don't prefix an already prefixed table
615 614
 		if (strpos($str, $this->tablePrefix) !== FALSE)
@@ -626,9 +625,9 @@ abstract class AbstractDriver extends PDO implements DriverInterface {
626 625
 	 * @param string $table
627 626
 	 * @return PDOStatement
628 627
 	 */
629
-	public function truncate($table)
628
+	public function truncate($table): PDOStatement
630 629
 	{
631
-		$sql = ($this->hasTruncate)
630
+		$sql = $this->hasTruncate
632 631
 			? 'TRUNCATE TABLE '
633 632
 			: 'DELETE FROM ';
634 633
 

+ 9
- 9
src/Drivers/AbstractUtil.php View File

@@ -26,16 +26,16 @@ abstract class AbstractUtil {
26 26
 	 * Reference to the current connection object
27 27
 	 * @var DriverInterface
28 28
 	 */
29
-	private $conn;
29
+	private $connection;
30 30
 
31 31
 	/**
32 32
 	 * Save a reference to the connection object for later use
33 33
 	 *
34
-	 * @param DriverInterface $conn
34
+	 * @param DriverInterface $connection
35 35
 	 */
36
-	public function __construct(DriverInterface $conn)
36
+	public function __construct(DriverInterface $connection)
37 37
 	{
38
-		$this->conn = $conn;
38
+		$this->connection = $connection;
39 39
 	}
40 40
 
41 41
 	/**
@@ -45,7 +45,7 @@ abstract class AbstractUtil {
45 45
 	 */
46 46
 	public function getDriver()
47 47
 	{
48
-		return $this->conn;
48
+		return $this->connection;
49 49
 	}
50 50
 
51 51
 	/**
@@ -59,7 +59,7 @@ abstract class AbstractUtil {
59 59
 	 */
60 60
 	public function createTable($name, $fields, array $constraints=[], $ifNotExists=TRUE)
61 61
 	{
62
-		$existsStr = ($ifNotExists) ? ' IF NOT EXISTS ' : ' ';
62
+		$existsStr = $ifNotExists ? ' IF NOT EXISTS ' : ' ';
63 63
 
64 64
 		// Reorganize into an array indexed with column information
65 65
 		// Eg $columnArray[$colname] = array(
@@ -77,8 +77,8 @@ abstract class AbstractUtil {
77 77
 		foreach($columnArray as $n => $props)
78 78
 		{
79 79
 			$str = $this->getDriver()->quoteIdent($n);
80
-			$str .= (isset($props['type'])) ? " {$props['type']}" : "";
81
-			$str .= (isset($props['constraint'])) ? " {$props['constraint']}" : "";
80
+			$str .= isset($props['type']) ? " {$props['type']}" : "";
81
+			$str .= isset($props['constraint']) ? " {$props['constraint']}" : "";
82 82
 
83 83
 			$columns[] = $str;
84 84
 		}
@@ -97,7 +97,7 @@ abstract class AbstractUtil {
97 97
 	 * @param string $name
98 98
 	 * @return string
99 99
 	 */
100
-	public function deleteTable($name)
100
+	public function deleteTable($name): string
101 101
 	{
102 102
 		return 'DROP TABLE IF EXISTS '.$this->getDriver()->quoteTable($name);
103 103
 	}

+ 0
- 2
src/Drivers/DriverInterface.php View File

@@ -152,8 +152,6 @@ interface DriverInterface extends PDOInterface {
152 152
 	 */
153 153
 	public function prepareExecute($sql, $params);
154 154
 
155
-
156
-
157 155
 	/**
158 156
 	 * Method to simplify retrieving db results for meta-data queries
159 157
 	 *

+ 1
- 1
src/Drivers/Mysql/Driver.php View File

@@ -49,7 +49,7 @@ class Driver extends AbstractDriver implements DriverInterface {
49 49
 	public function __construct($dsn, $username=NULL, $password=NULL, array $options=[])
50 50
 	{
51 51
 		// Set the charset to UTF-8
52
-		if (defined('\\PDO::MYSQL_ATTR_INIT_COMMAND'))
52
+		if (\defined('\\PDO::MYSQL_ATTR_INIT_COMMAND'))
53 53
 		{
54 54
 			$options = array_merge($options, [
55 55
 				PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES UTF-8 COLLATE 'UTF-8'",

+ 3
- 3
src/Drivers/Mysql/Util.php View File

@@ -27,7 +27,7 @@ class Util extends AbstractUtil {
27 27
 	 *
28 28
 	 * @return string
29 29
 	 */
30
-	public function backupStructure()
30
+	public function backupStructure(): string
31 31
 	{
32 32
 		$string = [];
33 33
 
@@ -37,7 +37,7 @@ class Util extends AbstractUtil {
37 37
 		foreach($dbs as &$d)
38 38
 		{
39 39
 			// Skip built-in dbs
40
-			if ($d == 'mysql')
40
+			if ($d === 'mysql')
41 41
 			{
42 42
 				continue;
43 43
 			}
@@ -69,7 +69,7 @@ class Util extends AbstractUtil {
69 69
 	 * @param array $exclude
70 70
 	 * @return string
71 71
 	 */
72
-	public function backupData($exclude=[])
72
+	public function backupData($exclude=[]): string
73 73
 	{
74 74
 		$tables = $this->getDriver()->getTables();
75 75
 

+ 4
- 6
src/Drivers/Pgsql/Driver.php View File

@@ -48,7 +48,7 @@ class Driver extends AbstractDriver implements DriverInterface {
48 48
 	 *
49 49
 	 * @return array
50 50
 	 */
51
-	public function getSchemas()
51
+	public function getSchemas(): ?array
52 52
 	{
53 53
 		$sql = <<<SQL
54 54
 			SELECT DISTINCT "schemaname" FROM "pg_tables"
@@ -67,7 +67,7 @@ SQL;
67 67
 	 * @param string $table
68 68
 	 * @return array
69 69
 	 */
70
-	public function getFks($table)
70
+	public function getFks($table): array
71 71
 	{
72 72
 		$valueMap = [
73 73
 			'c' => 'CASCADE',
@@ -80,10 +80,8 @@ SQL;
80 80
 		{
81 81
 			foreach(['update', 'delete'] AS $type)
82 82
 			{
83
-				if ( ! isset($valueMap[$key[$type]]))
84
-				{
85
-					continue;
86
-				}
83
+				if ( ! isset($valueMap[$key[$type]])) continue;
84
+
87 85
 
88 86
 				$key[$type] = $valueMap[$key[$type]];
89 87
 			}

+ 3
- 3
src/Drivers/Pgsql/Util.php View File

@@ -17,7 +17,7 @@ namespace Query\Drivers\Pgsql;
17 17
 use Query\Drivers\AbstractUtil;
18 18
 
19 19
 /**
20
- * Posgres-specific backup, import and creation methods
20
+ * Postgres-specific backup, import and creation methods
21 21
  */
22 22
 class Util extends AbstractUtil {
23 23
 
@@ -26,7 +26,7 @@ class Util extends AbstractUtil {
26 26
 	 *
27 27
 	 * @return string
28 28
 	 */
29
-	public function backupStructure()
29
+	public function backupStructure(): string
30 30
 	{
31 31
 		// @TODO Implement Backup function
32 32
 		return '';
@@ -38,7 +38,7 @@ class Util extends AbstractUtil {
38 38
 	 * @param array $exclude
39 39
 	 * @return string
40 40
 	 */
41
-	public function backupData($exclude=[])
41
+	public function backupData(array $exclude=[]): string
42 42
 	{
43 43
 		$tables = $this->getDriver()->getTables();
44 44
 

+ 2
- 2
src/Drivers/Sqlite/Driver.php View File

@@ -61,7 +61,7 @@ class Driver extends AbstractDriver implements DriverInterface {
61 61
 	 *
62 62
 	 * @return mixed
63 63
 	 */
64
-	public function getTables()
64
+	public function getTables(): array
65 65
 	{
66 66
 		$sql = $this->sql->tableList();
67 67
 		$res = $this->query($sql);
@@ -74,7 +74,7 @@ class Driver extends AbstractDriver implements DriverInterface {
74 74
 	 * @param string $table
75 75
 	 * @return array
76 76
 	 */
77
-	public function getFks($table)
77
+	public function getFks($table): array
78 78
 	{
79 79
 		$returnRows = [];
80 80
 

+ 2
- 2
src/Drivers/Sqlite/Util.php View File

@@ -33,7 +33,7 @@ class Util extends AbstractUtil {
33 33
 	 * @param array $excluded
34 34
 	 * @return string
35 35
 	 */
36
-	public function backupData($excluded=[])
36
+	public function backupData(array $excluded=[]): string
37 37
 	{
38 38
 		// Get a list of all the objects
39 39
 		$sql = 'SELECT DISTINCT "name"
@@ -103,7 +103,7 @@ class Util extends AbstractUtil {
103 103
 	 *
104 104
 	 * @return string
105 105
 	 */
106
-	public function backupStructure()
106
+	public function backupStructure(): string
107 107
 	{
108 108
 		// Fairly easy for SQLite...just query the master table
109 109
 		$sql = 'SELECT "sql" FROM "sqlite_master"';

+ 630
- 32
src/QueryBuilder.php View File

@@ -16,12 +16,172 @@ namespace Query;
16 16
 
17 17
 use BadMethodCallException;
18 18
 use PDOStatement;
19
-use Query\Drivers\DriverInterface;
19
+use Query\Drivers\{
20
+	AbstractUtil,
21
+	DriverInterface,
22
+	SQLInterface
23
+};
20 24
 
21 25
 /**
22 26
  * Convenience class for creating sql queries
27
+ * @method query(mixed $sql): PDOStatement;
23 28
  */
24
-class QueryBuilder extends AbstractQueryBuilder implements QueryBuilderInterface {
29
+class QueryBuilder implements QueryBuilderInterface {
30
+
31
+	// --------------------------------------------------------------------------
32
+	// ! Constants
33
+	// --------------------------------------------------------------------------
34
+
35
+	const KEY 	= 0;
36
+	const VALUE = 1;
37
+	const BOTH 	= 2;
38
+
39
+	// --------------------------------------------------------------------------
40
+	// ! SQL Clause Strings
41
+	// --------------------------------------------------------------------------
42
+
43
+	/**
44
+	 * Compiled 'select' clause
45
+	 * @var string
46
+	 */
47
+	protected $selectString = '';
48
+
49
+	/**
50
+	 * Compiled 'from' clause
51
+	 * @var string
52
+	 */
53
+	protected $fromString = '';
54
+
55
+	/**
56
+	 * Compiled arguments for insert / update
57
+	 * @var string
58
+	 */
59
+	protected $setString;
60
+
61
+	/**
62
+	 * Order by clause
63
+	 * @var string
64
+	 */
65
+	protected $orderString;
66
+
67
+	/**
68
+	 * Group by clause
69
+	 * @var string
70
+	 */
71
+	protected $groupString;
72
+
73
+	// --------------------------------------------------------------------------
74
+	// ! SQL Clause Arrays
75
+	// --------------------------------------------------------------------------
76
+
77
+	/**
78
+	 * Keys for insert/update statement
79
+	 * @var array
80
+	 */
81
+	protected $setArrayKeys = [];
82
+
83
+	/**
84
+	 * Key/val pairs for order by clause
85
+	 * @var array
86
+	 */
87
+	protected $orderArray = [];
88
+
89
+	/**
90
+	 * Key/val pairs for group by clause
91
+	 * @var array
92
+	 */
93
+	protected $groupArray = [];
94
+
95
+	// --------------------------------------------------------------------------
96
+	// ! Other Class vars
97
+	// --------------------------------------------------------------------------
98
+
99
+	/**
100
+	 * Values to apply to prepared statements
101
+	 * @var array
102
+	 */
103
+	protected $values = [];
104
+
105
+	/**
106
+	 * Values to apply to where clauses in prepared statements
107
+	 * @var array
108
+	 */
109
+	protected $whereValues = [];
110
+
111
+	/**
112
+	 * Value for limit string
113
+	 * @var string
114
+	 */
115
+	protected $limit;
116
+
117
+	/**
118
+	 * Value for offset in limit string
119
+	 * @var integer
120
+	 */
121
+	protected $offset;
122
+
123
+	/**
124
+	 * Query component order mapping
125
+	 * for complex select queries
126
+	 *
127
+	 * Format:
128
+	 * array(
129
+	 *		'type' => 'where',
130
+	 *		'conjunction' => ' AND ',
131
+	 *		'string' => 'k=?'
132
+	 * )
133
+	 *
134
+	 * @var array
135
+	 */
136
+	protected $queryMap = [];
137
+
138
+	/**
139
+	 * Map for having clause
140
+	 * @var array
141
+	 */
142
+	protected $havingMap;
143
+
144
+	/**
145
+	 * Convenience property for connection management
146
+	 * @var string
147
+	 */
148
+	public $connName = '';
149
+
150
+	/**
151
+	 * List of queries executed
152
+	 * @var array
153
+	 */
154
+	public $queries;
155
+
156
+	/**
157
+	 * Whether to do only an explain on the query
158
+	 * @var boolean
159
+	 */
160
+	protected $explain;
161
+
162
+	/**
163
+	 * The current database driver
164
+	 * @var DriverInterface
165
+	 */
166
+	public $driver;
167
+
168
+	/**
169
+	 * Query parser class instance
170
+	 * @var QueryParser
171
+	 */
172
+	protected $parser;
173
+
174
+	/**
175
+	 * Alias to driver util class
176
+	 * @var AbstractUtil
177
+	 */
178
+	protected $util;
179
+
180
+	/**
181
+	 * Alias to driver sql class
182
+	 * @var SQLInterface
183
+	 */
184
+	protected $sql;
25 185
 
26 186
 	/**
27 187
 	 * String class values to be reset
@@ -61,20 +221,20 @@ class QueryBuilder extends AbstractQueryBuilder implements QueryBuilderInterface
61 221
 	/**
62 222
 	 * Constructor
63 223
 	 *
64
-	 * @param DriverInterface $db
224
+	 * @param DriverInterface $driver
65 225
 	 * @param QueryParser $parser
66 226
 	 */
67
-	public function __construct(DriverInterface $db, QueryParser $parser)
227
+	public function __construct(DriverInterface $driver, QueryParser $parser)
68 228
 	{
69 229
 		// Inject driver and parser
70
-		$this->db = $db;
230
+		$this->driver = $driver;
71 231
 		$this->parser = $parser;
72 232
 
73 233
 		$this->queries['total_time'] = 0;
74 234
 
75 235
 		// Alias driver sql and util classes
76
-		$this->sql = $this->db->getSql();
77
-		$this->util = $this->db->getUtil();
236
+		$this->sql = $this->driver->getSql();
237
+		$this->util = $this->driver->getUtil();
78 238
 	}
79 239
 
80 240
 	/**
@@ -83,7 +243,7 @@ class QueryBuilder extends AbstractQueryBuilder implements QueryBuilderInterface
83 243
 	 */
84 244
 	public function __destruct()
85 245
 	{
86
-		$this->db = NULL;
246
+		$this->driver = NULL;
87 247
 	}
88 248
 
89 249
 	/**
@@ -99,7 +259,7 @@ class QueryBuilder extends AbstractQueryBuilder implements QueryBuilderInterface
99 259
 		// Alias snake_case method calls
100 260
 		$camelName = \to_camel_case($name);
101 261
 
102
-		foreach([$this, $this->db] as $object)
262
+		foreach([$this, $this->driver] as $object)
103 263
 		{
104 264
 			foreach([$name, $camelName] as $methodName)
105 265
 			{
@@ -114,6 +274,15 @@ class QueryBuilder extends AbstractQueryBuilder implements QueryBuilderInterface
114 274
 		throw new BadMethodCallException('Method does not exist');
115 275
 	}
116 276
 
277
+	// --------------------------------------------------------------------------
278
+	// ! Driver setters
279
+	// --------------------------------------------------------------------------
280
+
281
+	public function setDriver(DriverInterface $driver)
282
+	{
283
+
284
+	}
285
+
117 286
 	// --------------------------------------------------------------------------
118 287
 	// ! Select Queries
119 288
 	// --------------------------------------------------------------------------
@@ -141,14 +310,14 @@ class QueryBuilder extends AbstractQueryBuilder implements QueryBuilderInterface
141 310
 		}
142 311
 
143 312
 		// Quote the identifiers
144
-		$safeArray = $this->db->quoteIdent($fieldsArray);
313
+		$safeArray = $this->driver->quoteIdent($fieldsArray);
145 314
 
146 315
 		unset($fieldsArray);
147 316
 
148 317
 		// Join the strings back together
149 318
 		for($i = 0, $c = count($safeArray); $i < $c; $i++)
150 319
 		{
151
-			if (is_array($safeArray[$i]))
320
+			if (\is_array($safeArray[$i]))
152 321
 			{
153 322
 				$safeArray[$i] = implode(' AS ', $safeArray[$i]);
154 323
 			}
@@ -253,8 +422,8 @@ class QueryBuilder extends AbstractQueryBuilder implements QueryBuilderInterface
253 422
 		$identArray = array_map('\\mb_trim', $identArray);
254 423
 
255 424
 		// Quote the identifiers
256
-		$identArray[0] = $this->db->quoteTable($identArray[0]);
257
-		$identArray = $this->db->quoteIdent($identArray);
425
+		$identArray[0] = $this->driver->quoteTable($identArray[0]);
426
+		$identArray = $this->driver->quoteIdent($identArray);
258 427
 
259 428
 		// Paste it back together
260 429
 		$this->fromString = implode(' ', $identArray);
@@ -443,7 +612,7 @@ class QueryBuilder extends AbstractQueryBuilder implements QueryBuilderInterface
443 612
 
444 613
 		// Use the keys of the array to make the insert/update string
445 614
 		// Escape the field names
446
-		$this->setArrayKeys = array_map([$this->db, '_quote'], $this->setArrayKeys);
615
+		$this->setArrayKeys = array_map([$this->driver, '_quote'], $this->setArrayKeys);
447 616
 
448 617
 		// Generate the "set" string
449 618
 		$this->setString = implode('=?,', $this->setArrayKeys);
@@ -464,8 +633,8 @@ class QueryBuilder extends AbstractQueryBuilder implements QueryBuilderInterface
464 633
 	{
465 634
 		// Prefix and quote table name
466 635
 		$table = explode(' ', mb_trim($table));
467
-		$table[0] = $this->db->quoteTable($table[0]);
468
-		$table = $this->db->quoteIdent($table);
636
+		$table[0] = $this->driver->quoteTable($table[0]);
637
+		$table = $this->driver->quoteIdent($table);
469 638
 		$table = implode(' ', $table);
470 639
 
471 640
 		// Parse out the join condition
@@ -487,12 +656,12 @@ class QueryBuilder extends AbstractQueryBuilder implements QueryBuilderInterface
487 656
 	{
488 657
 		if ( ! is_scalar($field))
489 658
 		{
490
-			$newGroupArray = array_map([$this->db, 'quoteIdent'], $field);
659
+			$newGroupArray = array_map([$this->driver, 'quoteIdent'], $field);
491 660
 			$this->groupArray = array_merge($this->groupArray, $newGroupArray);
492 661
 		}
493 662
 		else
494 663
 		{
495
-			$this->groupArray[] = $this->db->quoteIdent($field);
664
+			$this->groupArray[] = $this->driver->quoteIdent($field);
496 665
 		}
497 666
 
498 667
 		$this->groupString = ' GROUP BY ' . implode(',', $this->groupArray);
@@ -507,7 +676,7 @@ class QueryBuilder extends AbstractQueryBuilder implements QueryBuilderInterface
507 676
 	 * @param string $type
508 677
 	 * @return QueryBuilderInterface
509 678
 	 */
510
-	public function orderBy($field, $type=""): QueryBuilderInterface
679
+	public function orderBy($field, $type=''): QueryBuilderInterface
511 680
 	{
512 681
 		// When ordering by random, do an ascending order if the driver
513 682
 		// doesn't support random ordering
@@ -518,7 +687,7 @@ class QueryBuilder extends AbstractQueryBuilder implements QueryBuilderInterface
518 687
 		}
519 688
 
520 689
 		// Set fields for later manipulation
521
-		$field = $this->db->quoteIdent($field);
690
+		$field = $this->driver->quoteIdent($field);
522 691
 		$this->orderArray[$field] = $type;
523 692
 
524 693
 		$orderClauses = [];
@@ -563,7 +732,7 @@ class QueryBuilder extends AbstractQueryBuilder implements QueryBuilderInterface
563 732
 	 */
564 733
 	public function groupStart(): QueryBuilderInterface
565 734
 	{
566
-		$conj = (empty($this->queryMap)) ? ' WHERE ' : ' ';
735
+		$conj = empty($this->queryMap) ? ' WHERE ' : ' ';
567 736
 
568 737
 		$this->_appendMap($conj, '(', 'group_start');
569 738
 
@@ -578,7 +747,7 @@ class QueryBuilder extends AbstractQueryBuilder implements QueryBuilderInterface
578 747
 	 */
579 748
 	public function notGroupStart(): QueryBuilderInterface
580 749
 	{
581
-		$conj = (empty($this->queryMap)) ? ' WHERE ' : ' AND ';
750
+		$conj = empty($this->queryMap) ? ' WHERE ' : ' AND ';
582 751
 
583 752
 		$this->_appendMap($conj, ' NOT (', 'group_start');
584 753
 
@@ -679,8 +848,8 @@ class QueryBuilder extends AbstractQueryBuilder implements QueryBuilderInterface
679 848
 	 */
680 849
 	public function countAll($table): int
681 850
 	{
682
-		$sql = 'SELECT * FROM '.$this->db->quoteTable($table);
683
-		$res = $this->db->query($sql);
851
+		$sql = 'SELECT * FROM '.$this->driver->quoteTable($table);
852
+		$res = $this->driver->query($sql);
684 853
 		return (int) count($res->fetchAll());
685 854
 	}
686 855
 
@@ -720,7 +889,7 @@ class QueryBuilder extends AbstractQueryBuilder implements QueryBuilderInterface
720 889
 			$this->set($data);
721 890
 		}
722 891
 
723
-		return $this->_run("insert", $table);
892
+		return $this->_run('insert', $table);
724 893
 	}
725 894
 
726 895
 	/**
@@ -733,7 +902,7 @@ class QueryBuilder extends AbstractQueryBuilder implements QueryBuilderInterface
733 902
 	public function insertBatch($table, $data=[]): PDOStatement
734 903
 	{
735 904
 		// Get the generated values and sql string
736
-		list($sql, $data) = $this->db->insertBatch($table, $data);
905
+		list($sql, $data) = $this->driver->insertBatch($table, $data);
737 906
 
738 907
 		return ( ! is_null($sql))
739 908
 			? $this->_run('', $table, $sql, $data)
@@ -754,7 +923,7 @@ class QueryBuilder extends AbstractQueryBuilder implements QueryBuilderInterface
754 923
 			$this->set($data);
755 924
 		}
756 925
 
757
-		return $this->_run("update", $table);
926
+		return $this->_run('update', $table);
758 927
 	}
759 928
 
760 929
 	/**
@@ -769,7 +938,7 @@ class QueryBuilder extends AbstractQueryBuilder implements QueryBuilderInterface
769 938
 	public function updateBatch($table, $data, $where)
770 939
 	{
771 940
 		// Get the generated values and sql string
772
-		list($sql, $data) = $this->db->updateBatch($table, $data, $where);
941
+		list($sql, $data) = $this->driver->updateBatch($table, $data, $where);
773 942
 
774 943
 		return ( ! is_null($sql))
775 944
 			? $this->_run('', $table, $sql, $data)
@@ -790,7 +959,7 @@ class QueryBuilder extends AbstractQueryBuilder implements QueryBuilderInterface
790 959
 			$this->set($data);
791 960
 		}
792 961
 
793
-		return $this->_run("replace", $table);
962
+		return $this->_run('replace', $table);
794 963
 	}
795 964
 
796 965
 	/**
@@ -808,7 +977,7 @@ class QueryBuilder extends AbstractQueryBuilder implements QueryBuilderInterface
808 977
 			$this->where($where);
809 978
 		}
810 979
 
811
-		return $this->_run("delete", $table);
980
+		return $this->_run('delete', $table);
812 981
 	}
813 982
 
814 983
 	// --------------------------------------------------------------------------
@@ -878,7 +1047,7 @@ class QueryBuilder extends AbstractQueryBuilder implements QueryBuilderInterface
878 1047
 	 *
879 1048
 	 * @return void
880 1049
 	 */
881
-	public function resetQuery()
1050
+	public function resetQuery(): void
882 1051
 	{
883 1052
 		// Reset strings and booleans
884 1053
 		foreach($this->stringVars as $var)
@@ -892,5 +1061,434 @@ class QueryBuilder extends AbstractQueryBuilder implements QueryBuilderInterface
892 1061
 			$this->$var = [];
893 1062
 		}
894 1063
 	}
1064
+
1065
+	/**
1066
+	 * Set values in the class, with either an array or key value pair
1067
+	 *
1068
+	 * @param array $var
1069
+	 * @param mixed $key
1070
+	 * @param mixed $val
1071
+	 * @param int $valType
1072
+	 * @return array
1073
+	 */
1074
+	protected function _mixedSet(array &$var, $key, $val=NULL, int $valType=self::BOTH): array
1075
+	{
1076
+		$arg = (is_scalar($key) && is_scalar($val))
1077
+			? [$key => $val]
1078
+			: $key;
1079
+
1080
+		foreach($arg as $k => $v)
1081
+		{
1082
+			if (\in_array($valType, [self::KEY, self::VALUE], TRUE))
1083
+			{
1084
+				$var[] = ($valType === self::KEY)
1085
+					? $k
1086
+					: $v;
1087
+			}
1088
+			else
1089
+			{
1090
+				$var[$k] = $v;
1091
+			}
1092
+		}
1093
+
1094
+		return $var;
1095
+	}
1096
+
1097
+	/**
1098
+	 * Method to simplify select_ methods
1099
+	 *
1100
+	 * @param string $field
1101
+	 * @param string|bool $as
1102
+	 * @return string
1103
+	 */
1104
+	protected function _select(string $field, $as = FALSE): string
1105
+	{
1106
+		// Escape the identifiers
1107
+		$field = $this->driver->quoteIdent($field);
1108
+
1109
+		if ( ! \is_string($as))
1110
+		{
1111
+			return $field;
1112
+		}
1113
+
1114
+		$as = $this->driver->quoteIdent($as);
1115
+		return "({$field}) AS {$as} ";
1116
+	}
1117
+
1118
+	/**
1119
+	 * Helper function for returning sql strings
1120
+	 *
1121
+	 * @param string $type
1122
+	 * @param string $table
1123
+	 * @param bool $reset
1124
+	 * @return string
1125
+	 */
1126
+	protected function _getCompile(string $type, string $table, bool $reset): string
1127
+	{
1128
+		$sql = $this->_compile($type, $table);
1129
+
1130
+		// Reset the query builder for the next query
1131
+		if ($reset)
1132
+		{
1133
+			$this->resetQuery();
1134
+		}
1135
+
1136
+		return $sql;
1137
+	}
1138
+
1139
+	/**
1140
+	 * Simplify 'like' methods
1141
+	 *
1142
+	 * @param string $field
1143
+	 * @param mixed $val
1144
+	 * @param string $pos
1145
+	 * @param string $like
1146
+	 * @param string $conj
1147
+	 * @return self
1148
+	 */
1149
+	protected function _like(string $field, $val, string $pos, string $like='LIKE', string $conj='AND'): self
1150
+	{
1151
+		$field = $this->driver->quoteIdent($field);
1152
+
1153
+		// Add the like string into the order map
1154
+		$like = $field. " {$like} ?";
1155
+
1156
+		if ($pos === 'before')
1157
+		{
1158
+			$val = "%{$val}";
1159
+		}
1160
+		elseif ($pos === 'after')
1161
+		{
1162
+			$val = "{$val}%";
1163
+		}
1164
+		else
1165
+		{
1166
+			$val = "%{$val}%";
1167
+		}
1168
+
1169
+		$conj = empty($this->queryMap) ? ' WHERE ' : " {$conj} ";
1170
+		$this->_appendMap($conj, $like, 'like');
1171
+
1172
+		// Add to the values array
1173
+		$this->whereValues[] = $val;
1174
+
1175
+		return $this;
1176
+	}
1177
+
1178
+	/**
1179
+	 * Simplify building having clauses
1180
+	 *
1181
+	 * @param mixed $key
1182
+	 * @param mixed $values
1183
+	 * @param string $conj
1184
+	 * @return self
1185
+	 */
1186
+	protected function _having($key, $values=[], string $conj='AND'): self
1187
+	{
1188
+		$where = $this->_where($key, $values);
1189
+
1190
+		// Create key/value placeholders
1191
+		foreach($where as $f => $val)
1192
+		{
1193
+			// Split each key by spaces, in case there
1194
+			// is an operator such as >, <, !=, etc.
1195
+			$fArray = explode(' ', trim($f));
1196
+
1197
+			$item = $this->driver->quoteIdent($fArray[0]);
1198
+
1199
+			// Simple key value, or an operator
1200
+			$item .= (count($fArray) === 1) ? '=?' : " {$fArray[1]} ?";
1201
+
1202
+			// Put in the having map
1203
+			$this->havingMap[] = [
1204
+				'conjunction' => ( ! empty($this->havingMap)) ? " {$conj} " : ' HAVING ',
1205
+				'string' => $item
1206
+			];
1207
+		}
1208
+
1209
+		return $this;
1210
+	}
1211
+
1212
+	/**
1213
+	 * Do all the redundant stuff for where/having type methods
1214
+	 *
1215
+	 * @param mixed $key
1216
+	 * @param mixed $val
1217
+	 * @return array
1218
+	 */
1219
+	protected function _where($key, $val=[]): array
1220
+	{
1221
+		$where = [];
1222
+		$this->_mixedSet($where, $key, $val);
1223
+		$this->_mixedSet($this->whereValues, $key, $val, self::VALUE);
1224
+		return $where;
1225
+	}
1226
+
1227
+	/**
1228
+	 * Simplify generating where string
1229
+	 *
1230
+	 * @param mixed $key
1231
+	 * @param mixed $values
1232
+	 * @param string $defaultConj
1233
+	 * @return self
1234
+	 */
1235
+	protected function _whereString($key, $values=[], string $defaultConj='AND'): self
1236
+	{
1237
+		// Create key/value placeholders
1238
+		foreach($this->_where($key, $values) as $f => $val)
1239
+		{
1240
+			// Split each key by spaces, in case there
1241
+			// is an operator such as >, <, !=, etc.
1242
+			$fArray = explode(' ', trim($f));
1243
+
1244
+			$item = $this->driver->quoteIdent($fArray[0]);
1245
+
1246
+			// Simple key value, or an operator
1247
+			$item .= (count($fArray) === 1) ? '=?' : " {$fArray[1]} ?";
1248
+			$lastItem = end($this->queryMap);
1249
+
1250
+			// Determine the correct conjunction
1251
+			$conjunctionList = array_column($this->queryMap, 'conjunction');
1252
+			if (empty($this->queryMap) || ( ! regex_in_array($conjunctionList, "/^ ?\n?WHERE/i")))
1253
+			{
1254
+				$conj = "\nWHERE ";
1255
+			}
1256
+			elseif ($lastItem['type'] === 'group_start')
1257
+			{
1258
+				$conj = '';
1259
+			}
1260
+			else
1261
+			{
1262
+				$conj = " {$defaultConj} ";
1263
+			}
1264
+
1265
+			$this->_appendMap($conj, $item, 'where');
1266
+		}
1267
+
1268
+		return $this;
1269
+	}
1270
+
1271
+	/**
1272
+	 * Simplify where_in methods
1273
+	 *
1274
+	 * @param mixed $key
1275
+	 * @param mixed $val
1276
+	 * @param string $in - The (not) in fragment
1277
+	 * @param string $conj - The where in conjunction
1278
+	 * @return self
1279
+	 */
1280
+	protected function _whereIn($key, $val=[], string $in='IN', string $conj='AND'): self
1281
+	{
1282
+		$key = $this->driver->quoteIdent($key);
1283
+		$params = array_fill(0, count($val), '?');
1284
+
1285
+		foreach($val as $v)
1286
+		{
1287
+			$this->whereValues[] = $v;
1288
+		}
1289
+
1290
+		$conjunction = ( ! empty($this->queryMap)) ? " {$conj} " : ' WHERE ';
1291
+		$str = $key . " {$in} (".implode(',', $params).') ';
1292
+
1293
+		$this->_appendMap($conjunction, $str, 'where_in');
1294
+
1295
+		return $this;
1296
+	}
1297
+
1298
+	/**
1299
+	 * Executes the compiled query
1300
+	 *
1301
+	 * @param string $type
1302
+	 * @param string $table
1303
+	 * @param string $sql
1304
+	 * @param array|null $vals
1305
+	 * @param boolean $reset
1306
+	 * @return PDOStatement
1307
+	 */
1308
+	protected function _run(string $type, string $table, $sql=NULL, $vals=NULL, bool $reset=TRUE): PDOStatement
1309
+	{
1310
+		if ($sql === NULL)
1311
+		{
1312
+			$sql = $this->_compile($type, $table);
1313
+		}
1314
+
1315
+		if ($vals === NULL)
1316
+		{
1317
+			$vals = array_merge($this->values, (array) $this->whereValues);
1318
+		}
1319
+
1320
+		$startTime = microtime(TRUE);
1321
+
1322
+		$res = empty($vals)
1323
+			? $this->driver->query($sql)
1324
+			: $this->driver->prepareExecute($sql, $vals);
1325
+
1326
+		$endTime = microtime(TRUE);
1327
+		$totalTime = number_format($endTime - $startTime, 5);
1328
+
1329
+		// Add this query to the list of executed queries
1330
+		$this->_appendQuery($vals, $sql, (int) $totalTime);
1331
+
1332
+		// Reset class state for next query
1333
+		if ($reset)
1334
+		{
1335
+			$this->resetQuery();
1336
+		}
1337
+
1338
+		return $res;
1339
+	}
1340
+
1341
+	/**
1342
+	 * Add an additional set of mapping pairs to a internal map
1343
+	 *
1344
+	 * @param string $conjunction
1345
+	 * @param string $string
1346
+	 * @param string $type
1347
+	 * @return void
1348
+	 */
1349
+	protected function _appendMap(string $conjunction = '', string $string = '', string $type = '')
1350
+	{
1351
+		$this->queryMap[] = [
1352
+			'type' => $type,
1353
+			'conjunction' => $conjunction,
1354
+			'string' => $string
1355
+		];
1356
+	}
1357
+
1358
+	/**
1359
+	 * Convert the prepared statement into readable sql
1360
+	 *
1361
+	 * @param array $vals
1362
+	 * @param string $sql
1363
+	 * @param int $totalTime
1364
+	 * @return void
1365
+	 */
1366
+	protected function _appendQuery($vals, string $sql, int $totalTime)
1367
+	{
1368
+		$evals = \is_array($vals) ? $vals : [];
1369
+		$esql = str_replace('?', "%s", $sql);
1370
+
1371
+		// Quote string values
1372
+		foreach($evals as &$v)
1373
+		{
1374
+			$v = ( ! is_numeric($v))
1375
+				? htmlentities($this->driver->quote($v), ENT_NOQUOTES, 'utf-8')
1376
+				: $v;
1377
+		}
1378
+
1379
+		// Add the query onto the array of values to pass
1380
+		// as arguments to sprintf
1381
+		array_unshift($evals, $esql);
1382
+
1383
+		// Add the interpreted query to the list of executed queries
1384
+		$this->queries[] = [
1385
+			'time' => $totalTime,
1386
+			'sql' => sprintf(...$evals)
1387
+		];
1388
+
1389
+		$this->queries['total_time'] += $totalTime;
1390
+
1391
+		// Set the last query to get rowcounts properly
1392
+		$this->driver->setLastQuery($sql);
1393
+	}
1394
+
1395
+	/**
1396
+	 * Sub-method for generating sql strings
1397
+	 *
1398
+	 * @param string $type
1399
+	 * @param string $table
1400
+	 * @return string
1401
+	 */
1402
+	protected function _compileType(string $type='', string $table=''): string
1403
+	{
1404
+		switch($type)
1405
+		{
1406
+			case 'insert':
1407
+				$paramCount = count($this->setArrayKeys);
1408
+				$params = array_fill(0, $paramCount, '?');
1409
+				$sql = "INSERT INTO {$table} ("
1410
+					. implode(',', $this->setArrayKeys)
1411
+					. ")\nVALUES (".implode(',', $params).')';
1412
+				break;
1413
+
1414
+			case 'update':
1415
+				$sql = "UPDATE {$table}\nSET {$this->setString}";
1416
+				break;
1417
+
1418
+			case 'replace':
1419
+				// @TODO implement
1420
+				$sql = '';
1421
+				break;
1422
+
1423
+			case 'delete':
1424
+				$sql = "DELETE FROM {$table}";
1425
+				break;
1426
+
1427
+			// Get queries
1428
+			default:
1429
+				$sql = "SELECT * \nFROM {$this->fromString}";
1430
+
1431
+				// Set the select string
1432
+				if ( ! empty($this->selectString))
1433
+				{
1434
+					// Replace the star with the selected fields
1435
+					$sql = str_replace('*', $this->selectString, $sql);
1436
+				}
1437
+				break;
1438
+		}
1439
+
1440
+		return $sql;
1441
+	}
1442
+
1443
+	/**
1444
+	 * String together the sql statements for sending to the db
1445
+	 *
1446
+	 * @param string $type
1447
+	 * @param string $table
1448
+	 * @return string
1449
+	 */
1450
+	protected function _compile(string $type='', string $table=''): string
1451
+	{
1452
+		// Get the base clause for the query
1453
+		$sql = $this->_compileType($type, $this->driver->quoteTable($table));
1454
+
1455
+		$clauses = [
1456
+			'queryMap',
1457
+			'groupString',
1458
+			'orderString',
1459
+			'havingMap',
1460
+		];
1461
+
1462
+		// Set each type of subclause
1463
+		foreach($clauses as $clause)
1464
+		{
1465
+			$param = $this->$clause;
1466
+			if (\is_array($param))
1467
+			{
1468
+				foreach($param as $q)
1469
+				{
1470
+					$sql .= $q['conjunction'] . $q['string'];
1471
+				}
1472
+			}
1473
+			else
1474
+			{
1475
+				$sql .= $param;
1476
+			}
1477
+		}
1478
+
1479
+		// Set the limit via the class variables
1480
+		if (is_numeric($this->limit))
1481
+		{
1482
+			$sql = $this->sql->limit($sql, $this->limit, $this->offset);
1483
+		}
1484
+
1485
+		// See if the query plan, rather than the
1486
+		// query data should be returned
1487
+		if ($this->explain === TRUE)
1488
+		{
1489
+			$sql = $this->sql->explain($sql);
1490
+		}
1491
+
1492
+		return $sql;
1493
+	}
895 1494
 }
896
-// End of query_builder.php

+ 1
- 1
src/QueryBuilderInterface.php View File

@@ -527,7 +527,7 @@ interface QueryBuilderInterface {
527 527
 	 *
528 528
 	 * @return void
529 529
 	 */
530
-	public function resetQuery();
530
+	public function resetQuery(): void;
531 531
 }
532 532
 
533 533
 // End of QueryBuilderInterface.php

+ 9
- 0
tests/BaseQueryBuilderTest.php View File

@@ -62,6 +62,8 @@ abstract class BaseQueryBuilderTest extends TestCase {
62 62
 		$query = self::$db->get('test');
63 63
 
64 64
 		$this->assertIsA($query, 'PDOStatement');
65
+		$lastQuery = self::$db->getLastQuery();
66
+		$this->assertTrue(\is_string($lastQuery));
65 67
 	}
66 68
 
67 69
 	public function testPrefixGet()
@@ -519,6 +521,7 @@ abstract class BaseQueryBuilderTest extends TestCase {
519 521
 			->insert('test');
520 522
 
521 523
 		$this->assertIsA($query, 'PDOStatement');
524
+		$this->assertTrue(self::$db->affectedRows() > 0);
522 525
 	}
523 526
 
524 527
 	public function testInsertArray()
@@ -569,6 +572,12 @@ abstract class BaseQueryBuilderTest extends TestCase {
569 572
 		$this->assertIsA($query, 'PDOStatement');
570 573
 	}
571 574
 
575
+	public function testUpdateBatch()
576
+	{
577
+		$query = self::$db->updateBatch('test', [], '');
578
+		$this->assertNull($query);
579
+	}
580
+
572 581
 	public function testSetArrayUpdate()
573 582
 	{
574 583
 		$array = array(

+ 2
- 2
tests/Drivers/MySQL/MySQLDriverTest.php View File

@@ -51,7 +51,7 @@ class MySQLDriverTest extends BaseDriverTest {
51 51
 
52 52
 	public function testExists()
53 53
 	{
54
-		$this->assertTrue(in_array('mysql', PDO::getAvailableDrivers()));
54
+		$this->assertTrue(\in_array('mysql', PDO::getAvailableDrivers(), TRUE));
55 55
 	}
56 56
 
57 57
 	// --------------------------------------------------------------------------
@@ -97,7 +97,7 @@ class MySQLDriverTest extends BaseDriverTest {
97 97
 		//Check
98 98
 		$dbs = self::$db->getTables();
99 99
 
100
-		$this->assertTrue(in_array('create_test', $dbs));
100
+		$this->assertTrue(\in_array('create_test', $dbs, TRUE));
101 101
 
102 102
 	}
103 103
 

+ 6
- 6
tests/Drivers/PgSQL/PgSQLQueryBuilderTest.php View File

@@ -43,11 +43,11 @@ class PgSQLQueryBuilderTest extends BaseQueryBuilderTest {
43 43
 		else if ($params !== FALSE)
44 44
 		{
45 45
 			$params = $params->pgsql;
46
-			$params->type = "pgsql";
46
+			$params->type = 'pgsql';
47 47
 			//$params->port = 5432;
48 48
 			//$params->prefix = 'create_';
49 49
 			$params->options = array();
50
-			$params->options[\PDO::ATTR_PERSISTENT] = TRUE;
50
+			$params->options[PDO::ATTR_PERSISTENT] = TRUE;
51 51
 		}
52 52
 
53 53
 		self::$db = Query($params);
@@ -56,7 +56,7 @@ class PgSQLQueryBuilderTest extends BaseQueryBuilderTest {
56 56
 	public function setUp()
57 57
  	{
58 58
  		// If the database isn't installed, skip the tests
59
-		if ( ! in_array('pgsql', PDO::getAvailableDrivers()))
59
+		if ( ! \in_array('pgsql', PDO::getAvailableDrivers(), TRUE))
60 60
 		{
61 61
 			$this->markTestSkipped("Postgres extension for PDO not loaded");
62 62
 		}
@@ -66,7 +66,7 @@ class PgSQLQueryBuilderTest extends BaseQueryBuilderTest {
66 66
 
67 67
 	public function testExists()
68 68
 	{
69
-		$this->assertTrue(in_array('pgsql', PDO::getAvailableDrivers()));
69
+		$this->assertTrue(\in_array('pgsql', PDO::getAvailableDrivers(), TRUE));
70 70
 	}
71 71
 
72 72
 	// --------------------------------------------------------------------------
@@ -84,7 +84,7 @@ class PgSQLQueryBuilderTest extends BaseQueryBuilderTest {
84 84
 		// The exact results are version dependent
85 85
 		// The important thing is that there is an array
86 86
 		// of results returned
87
-		$this->assertTrue(is_array($res));
87
+		$this->assertTrue(\is_array($res));
88 88
 		$this->assertTrue(count($res) > 1);
89 89
 		$this->assertTrue(array_key_exists('QUERY PLAN', $res[0]));
90 90
 
@@ -117,6 +117,6 @@ class PgSQLQueryBuilderTest extends BaseQueryBuilderTest {
117 117
 
118 118
 	public function testBackupStructure()
119 119
 	{
120
-		$this->assertEquals('', self::$db->util->backupStructure());
120
+		$this->assertEquals('', self::$db->getUtil()->backupStructure());
121 121
 	}
122 122
 }

+ 4
- 4
tests/Drivers/SQLite/SQLiteDriverTest.php View File

@@ -183,7 +183,7 @@ SQL;
183 183
 		$db = new $class(QTEST_DIR.QDS.'db_files'.QDS.'test_sqlite.db');
184 184
 
185 185
 		$this->assertIsA($db, $class);
186
-		$this->assertIsA(self::$db->db, $class);
186
+		$this->assertIsA(self::$db->driver, $class);
187 187
 
188 188
 		unset($db);
189 189
 	}
@@ -286,13 +286,13 @@ SQL;
286 286
 
287 287
 	public function testNullMethods()
288 288
 	{
289
-		$sql = self::$db->sql->functionList();
289
+		$sql = self::$db->getSQL()->functionList();
290 290
 		$this->assertEqual(NULL, $sql);
291 291
 
292
-		$sql = self::$db->sql->procedureList();
292
+		$sql = self::$db->getSQL()->procedureList();
293 293
 		$this->assertEqual(NULL, $sql);
294 294
 
295
-		$sql = self::$db->sql->sequenceList();
295
+		$sql = self::$db->getSQL()->sequenceList();
296 296
 		$this->assertEqual(NULL, $sql);
297 297
 	}
298 298
 

+ 4
- 0
tests/QueryParserTest.php View File

@@ -21,6 +21,10 @@ use Query\Drivers\Sqlite\Driver;
21 21
  * Tests for the Query Parser
22 22
  */
23 23
 class QueryParserTest extends TestCase {
24
+	/**
25
+	 * @var QueryParser
26
+	 */
27
+	protected $parser;
24 28
 
25 29
 	public function setUp()
26 30
 	{