2016-10-12 22:12:25 -04:00
|
|
|
<?php declare(strict_types=1);
|
2012-08-02 11:59:11 -04:00
|
|
|
/**
|
|
|
|
* Query
|
|
|
|
*
|
2016-09-07 13:17:17 -04:00
|
|
|
* SQL Query Builder / Database Abstraction Layer
|
2012-08-02 11:59:11 -04:00
|
|
|
*
|
2019-12-11 16:49:42 -05:00
|
|
|
* PHP version 7.2
|
2016-09-07 13:17:17 -04:00
|
|
|
*
|
|
|
|
* @package Query
|
|
|
|
* @author Timothy J. Warren <tim@timshomepage.net>
|
2020-03-18 11:31:56 -04:00
|
|
|
* @copyright 2012 - 2020 Timothy J. Warren
|
2016-09-07 13:17:17 -04:00
|
|
|
* @license http://www.opensource.org/licenses/mit-license.html MIT License
|
2019-12-11 16:49:42 -05:00
|
|
|
* @link https://git.timshomepage.net/aviat/Query
|
|
|
|
* @version 3.0.0
|
2012-08-02 11:59:11 -04:00
|
|
|
*/
|
2014-04-02 17:08:50 -04:00
|
|
|
namespace Query;
|
|
|
|
|
2016-09-07 17:39:19 -04:00
|
|
|
use Query\Drivers\DriverInterface;
|
|
|
|
|
2012-08-02 11:59:11 -04:00
|
|
|
/**
|
|
|
|
* Utility Class to parse sql clauses for properly escaping identifiers
|
|
|
|
*/
|
2015-11-10 10:12:23 -05:00
|
|
|
class QueryParser {
|
2012-08-02 11:59:11 -04:00
|
|
|
|
2014-04-02 17:08:50 -04:00
|
|
|
/**
|
|
|
|
* DB Driver
|
|
|
|
*
|
2015-11-10 11:18:11 -05:00
|
|
|
* @var DriverInterface
|
2014-04-02 17:08:50 -04:00
|
|
|
*/
|
|
|
|
private $db;
|
|
|
|
|
2012-08-02 11:59:11 -04:00
|
|
|
/**
|
|
|
|
* Regex patterns for various syntax components
|
|
|
|
*
|
|
|
|
* @var array
|
|
|
|
*/
|
2016-10-13 21:55:23 -04:00
|
|
|
private $matchPatterns = [
|
2012-08-09 12:15:36 -04:00
|
|
|
'function' => '([a-zA-Z0-9_]+\((.*?)\))',
|
|
|
|
'identifier' => '([a-zA-Z0-9_-]+\.?)+',
|
|
|
|
'operator' => '=|AND|&&?|~|\|\|?|\^|/|>=?|<=?|-|%|OR|\+|NOT|\!=?|<>|XOR'
|
2016-09-07 13:10:03 -04:00
|
|
|
];
|
2012-08-02 11:59:11 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Regex matches
|
|
|
|
*
|
|
|
|
* @var array
|
|
|
|
*/
|
2016-09-07 13:10:03 -04:00
|
|
|
public $matches = [
|
|
|
|
'functions' => [],
|
|
|
|
'identifiers' => [],
|
|
|
|
'operators' => [],
|
|
|
|
'combined' => [],
|
|
|
|
];
|
2012-08-02 11:59:11 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Constructor/entry point into parser
|
|
|
|
*
|
2016-09-07 17:39:19 -04:00
|
|
|
* @param DriverInterface $db
|
2012-08-02 11:59:11 -04:00
|
|
|
*/
|
2015-11-10 11:18:11 -05:00
|
|
|
public function __construct(DriverInterface $db)
|
2012-08-02 11:59:11 -04:00
|
|
|
{
|
2014-04-02 18:53:48 -04:00
|
|
|
$this->db = $db;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2015-07-31 10:24:34 -04:00
|
|
|
* Parser method for setting the parse string
|
2014-04-02 18:53:48 -04:00
|
|
|
*
|
|
|
|
* @param string $sql
|
2014-04-23 15:53:16 -04:00
|
|
|
* @return array
|
2014-04-02 18:53:48 -04:00
|
|
|
*/
|
2016-10-13 21:55:23 -04:00
|
|
|
public function parseJoin(string $sql): array
|
2014-04-02 18:53:48 -04:00
|
|
|
{
|
2012-08-09 12:15:36 -04:00
|
|
|
// Get sql clause components
|
2016-10-13 21:55:23 -04:00
|
|
|
preg_match_all('`'.$this->matchPatterns['function'].'`', $sql, $this->matches['functions'], PREG_SET_ORDER);
|
|
|
|
preg_match_all('`'.$this->matchPatterns['identifier'].'`', $sql, $this->matches['identifiers'], PREG_SET_ORDER);
|
|
|
|
preg_match_all('`'.$this->matchPatterns['operator'].'`', $sql, $this->matches['operators'], PREG_SET_ORDER);
|
2012-08-09 12:15:36 -04:00
|
|
|
|
|
|
|
// Get everything at once for ordering
|
2016-10-13 21:55:23 -04:00
|
|
|
$fullPattern = '`'.$this->matchPatterns['function'].'+|'.$this->matchPatterns['identifier'].'|('.$this->matchPatterns['operator'].')+`i';
|
|
|
|
preg_match_all($fullPattern, $sql, $this->matches['combined'], PREG_SET_ORDER);
|
2012-08-09 12:15:36 -04:00
|
|
|
|
|
|
|
// Go through the matches, and get the most relevant matches
|
2016-10-13 21:55:23 -04:00
|
|
|
$this->matches = array_map([$this, 'filterArray'], $this->matches);
|
2015-07-16 16:56:13 -04:00
|
|
|
|
2012-08-09 12:15:36 -04:00
|
|
|
return $this->matches;
|
|
|
|
}
|
|
|
|
|
2014-04-02 17:08:50 -04:00
|
|
|
/**
|
|
|
|
* Compiles a join condition after parsing
|
|
|
|
*
|
|
|
|
* @param string $condition
|
|
|
|
* @return string
|
|
|
|
*/
|
2016-10-13 21:55:23 -04:00
|
|
|
public function compileJoin(string $condition): string
|
2014-04-02 17:08:50 -04:00
|
|
|
{
|
2016-10-13 21:55:23 -04:00
|
|
|
$parts = $this->parseJoin($condition);
|
2014-04-02 17:08:50 -04:00
|
|
|
$count = count($parts['identifiers']);
|
|
|
|
|
|
|
|
// Go through and quote the identifiers
|
|
|
|
for($i=0; $i <= $count; $i++)
|
|
|
|
{
|
|
|
|
if (in_array($parts['combined'][$i], $parts['identifiers']) && ! is_numeric($parts['combined'][$i]))
|
|
|
|
{
|
2016-10-13 21:55:23 -04:00
|
|
|
$parts['combined'][$i] = $this->db->quoteIdent($parts['combined'][$i]);
|
2014-04-02 17:08:50 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return implode('', $parts['combined']);
|
|
|
|
}
|
|
|
|
|
2012-08-09 12:15:36 -04:00
|
|
|
/**
|
|
|
|
* Returns a more useful match array
|
|
|
|
*
|
2014-02-18 15:18:01 -05:00
|
|
|
* @param array $array
|
2012-08-09 12:15:36 -04:00
|
|
|
* @return array
|
|
|
|
*/
|
2016-10-13 21:55:23 -04:00
|
|
|
protected function filterArray(array $array): array
|
2012-08-09 12:15:36 -04:00
|
|
|
{
|
2016-10-13 21:55:23 -04:00
|
|
|
$newArray = [];
|
2012-08-09 12:15:36 -04:00
|
|
|
|
|
|
|
foreach($array as $row)
|
|
|
|
{
|
2016-10-13 21:55:23 -04:00
|
|
|
$newArray[] = (is_array($row)) ? $row[0] : $row;
|
2012-08-09 12:15:36 -04:00
|
|
|
}
|
|
|
|
|
2016-10-13 21:55:23 -04:00
|
|
|
return $newArray;
|
2012-08-02 11:59:11 -04:00
|
|
|
}
|
2016-10-13 21:55:23 -04:00
|
|
|
}
|