QueryParser::compileJoin()   A
last analyzed

Complexity

Conditions 4
Paths 3

Size

Total Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
nc 3
nop 1
dl 0
loc 16
rs 9.7333
c 0
b 0
f 0
1
<?php declare(strict_types=1);
2
/**
3
 * Query
4
 *
5
 * SQL Query Builder / Database Abstraction Layer
6
 *
7
 * PHP version 7.1
8
 *
9
 * @package     Query
10
 * @author      Timothy J. Warren <[email protected]>
11
 * @copyright   2012 - 2018 Timothy J. Warren
12
 * @license     http://www.opensource.org/licenses/mit-license.html  MIT License
13
 * @link        https://git.timshomepage.net/aviat4ion/Query
14
 */
15
namespace Query;
16
17
use Query\Drivers\DriverInterface;
18
19
/**
20
 * Utility Class to parse sql clauses for properly escaping identifiers
21
 */
22
class QueryParser {
23
24
	/**
25
	 * DB Driver
26
	 *
27
	 * @var DriverInterface
28
	 */
29
	private $db;
30
31
	/**
32
	 * Regex patterns for various syntax components
33
	 *
34
	 * @var array
35
	 */
36
	private $matchPatterns = [
37
		'function' => '([a-zA-Z0-9_]+\((.*?)\))',
38
		'identifier' => '([a-zA-Z0-9_-]+\.?)+',
39
		'operator' => '=|AND|&&?|~|\|\|?|\^|/|>=?|<=?|-|%|OR|\+|NOT|\!=?|<>|XOR'
40
	];
41
42
	/**
43
	 * Regex matches
44
	 *
45
	 * @var array
46
	 */
47
	public $matches = [
48
		'functions' => [],
49
		'identifiers' => [],
50
		'operators' => [],
51
		'combined' => [],
52
	];
53
54
	/**
55
	 * Constructor/entry point into parser
56
	 *
57
	 * @param DriverInterface $db
58
	 */
59
	public function __construct(DriverInterface $db)
60
	{
61
		$this->db = $db;
62
	}
63
64
	/**
65
	 * Parser method for setting the parse string
66
	 *
67
	 * @param string $sql
68
	 * @return array
69
	 */
70
	public function parseJoin(string $sql): array
71
	{
72
		// Get sql clause components
73
		preg_match_all('`'.$this->matchPatterns['function'].'`', $sql, $this->matches['functions'], PREG_SET_ORDER);
74
		preg_match_all('`'.$this->matchPatterns['identifier'].'`', $sql, $this->matches['identifiers'], PREG_SET_ORDER);
75
		preg_match_all('`'.$this->matchPatterns['operator'].'`', $sql, $this->matches['operators'], PREG_SET_ORDER);
76
77
		// Get everything at once for ordering
78
		$fullPattern = '`'.$this->matchPatterns['function'].'+|'.$this->matchPatterns['identifier'].'|('.$this->matchPatterns['operator'].')+`i';
79
		preg_match_all($fullPattern, $sql, $this->matches['combined'], PREG_SET_ORDER);
80
81
		// Go through the matches, and get the most relevant matches
82
		$this->matches = array_map([$this, 'filterArray'], $this->matches);
83
84
		return $this->matches;
85
	}
86
87
	/**
88
	 * Compiles a join condition after parsing
89
	 *
90
	 * @param string $condition
91
	 * @return string
92
	 */
93
	public function compileJoin(string $condition): string
94
	{
95
		$parts = $this->parseJoin($condition);
96
		$count = count($parts['identifiers']);
97
98
		// Go through and quote the identifiers
99
		for($i=0; $i <= $count; $i++)
100
		{
101
			if (in_array($parts['combined'][$i], $parts['identifiers']) && ! is_numeric($parts['combined'][$i]))
102
			{
103
				$parts['combined'][$i] = $this->db->quoteIdent($parts['combined'][$i]);
104
			}
105
		}
106
107
		return implode('', $parts['combined']);
108
	}
109
110
	/**
111
	 * Returns a more useful match array
112
	 *
113
	 * @param array $array
114
	 * @return array
115
	 */
116
	protected function filterArray(array $array): array
117
	{
118
		$newArray = [];
119
120
		foreach($array as $row)
121
		{
122
			$newArray[] =  (is_array($row)) ? $row[0] : $row;
123
		}
124
125
		return $newArray;
126
	}
127
}