Query/src/QueryParser.php

109 lines
2.8 KiB
PHP
Raw Normal View History

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
*
2022-09-29 11:33:08 -04:00
* PHP version 8.1
2016-09-07 13:17:17 -04:00
*
* @package Query
2023-01-20 11:30:51 -05:00
* @author Timothy J. Warren <tim@timshome.page>
* @copyright 2012 - 2023 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
2022-09-29 11:33:08 -04:00
* @version 4.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
/**
* Regex patterns for various syntax components
*/
private array $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
*/
public array $matches = [
2016-09-07 13:10:03 -04:00
'functions' => [],
'identifiers' => [],
'operators' => [],
'combined' => [],
];
2012-08-02 11:59:11 -04:00
/**
* Constructor/entry point into parser
*/
2023-01-13 13:14:28 -05:00
public function __construct(private readonly DriverInterface $db)
2012-08-02 11:59:11 -04:00
{
}
/**
* Parser method for setting the parse string
*
2023-01-13 13:14:28 -05:00
* @return array[]
*/
2016-10-13 21:55:23 -04:00
public function parseJoin(string $sql): array
{
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);
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
*/
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);
$count = is_countable($parts['identifiers']) ? count($parts['identifiers']) : 0;
2014-04-02 17:08:50 -04:00
// 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
*
2023-01-13 13:14:28 -05:00
* @return array
2012-08-09 12:15:36 -04:00
*/
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
}