1
|
|
|
<?php |
2
|
|
|
namespace Intersvyaz\SqlParser; |
3
|
|
|
|
4
|
|
|
class Parser |
5
|
|
|
{ |
6
|
|
|
|
7
|
|
|
/** |
8
|
|
|
* @var string Текст sql запроса, который надо преобразовать |
9
|
|
|
*/ |
10
|
|
|
private $sql; |
11
|
|
|
/** |
12
|
|
|
* @var array параметры, влияющие на парсинг sql запроса |
13
|
|
|
*/ |
14
|
|
|
private $params; |
15
|
|
|
/** |
16
|
|
|
* @var array "упрощённый" список параметров, для кеширования |
17
|
|
|
*/ |
18
|
|
|
private $simplifiedParams; |
19
|
|
|
|
20
|
|
|
/** |
21
|
|
|
* @param string $sql |
22
|
|
|
* @param array $params |
23
|
|
|
*/ |
24
|
19 |
|
public function __construct($sql, $params = []) |
25
|
|
|
{ |
26
|
19 |
|
if (substr($sql, -4) === '.sql') { |
27
|
|
|
$this->sql = file_get_contents($sql); |
28
|
|
|
} else { |
29
|
19 |
|
$this->sql = $sql; |
30
|
|
|
} |
31
|
|
|
|
32
|
19 |
|
$this->params = $params; |
33
|
19 |
|
$this->parseSql(); |
34
|
19 |
|
} |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* @return string готовый sql запрос |
38
|
|
|
*/ |
39
|
12 |
|
public function getSql() |
40
|
|
|
{ |
41
|
12 |
|
return $this->sql; |
42
|
|
|
} |
43
|
|
|
|
44
|
|
|
/** |
45
|
|
|
* @inheritdoc |
46
|
|
|
*/ |
47
|
12 |
|
public function __toString() |
48
|
|
|
{ |
49
|
12 |
|
return $this->getSql(); |
50
|
|
|
} |
51
|
|
|
|
52
|
|
|
/** |
53
|
|
|
* @return array "упрощённый" список параметров |
54
|
|
|
*/ |
55
|
7 |
|
public function getSimplifiedParams() |
56
|
|
|
{ |
57
|
7 |
|
if (!isset($this->simplifiedParams)) { |
58
|
7 |
|
$this->simplifiedParams = $this->simplifyParams($this->params); |
59
|
7 |
|
} |
60
|
|
|
|
61
|
7 |
|
return $this->simplifiedParams; |
62
|
|
|
} |
63
|
|
|
|
64
|
|
|
/** |
65
|
|
|
* Конвертирует параметры запроса из расширенного формата в параметры пригодные для \yii\db\Command::bindValues. |
66
|
|
|
* @param array $params Параметры построения запроса. |
67
|
|
|
* @return array |
68
|
|
|
*/ |
69
|
7 |
|
private function simplifyParams($params) |
70
|
|
|
{ |
71
|
7 |
|
if (empty($params)) { |
72
|
1 |
|
return $params; |
73
|
|
|
} |
74
|
|
|
|
75
|
6 |
|
$newParams = []; |
76
|
6 |
|
foreach ($params as $key => $value) { |
77
|
6 |
|
$key = ':' . ltrim($key, ':'); |
78
|
6 |
|
if (is_array($value)) { |
79
|
5 |
|
if (isset($value[0]) && is_array($value[0])) { |
80
|
1 |
|
foreach ($value[0] as $valKey => $valVal) { |
81
|
1 |
|
$newParams[$key . '_' . $valKey] = $valVal; |
82
|
1 |
|
} |
83
|
5 |
|
} elseif (!isset($value['bind']) || $value['bind'] === true) { |
84
|
2 |
|
if (isset($value[0]) && isset($value[1])) { |
85
|
1 |
|
$newParams[$key] = [$value[0], $value[1]]; |
86
|
2 |
|
} elseif (isset($value[0])) { |
87
|
1 |
|
$newParams[$key] = $value[0]; |
88
|
1 |
|
} |
89
|
2 |
|
} |
90
|
5 |
|
} else { |
91
|
1 |
|
$newParams[$key] = $value; |
92
|
|
|
} |
93
|
6 |
|
} |
94
|
|
|
|
95
|
6 |
|
return $newParams; |
96
|
|
|
} |
97
|
|
|
|
98
|
|
|
/** |
99
|
|
|
* Функция разбора и подготовки текста sql запроса. |
100
|
|
|
*/ |
101
|
19 |
|
private function parseSql() |
102
|
|
|
{ |
103
|
|
|
// Разбор многострочных комментариев |
104
|
19 |
|
if (preg_match_all('#/\*(\w+)(.+?)\*/#s', $this->sql, $matches)) { |
105
|
9 |
|
$count = count($matches[0]); |
106
|
9 |
|
for ($i = 0; $i < $count; $i++) { |
107
|
9 |
|
$this->replaceComment($matches[0][$i], $matches[2][$i], $matches[1][$i]); |
108
|
9 |
|
} |
109
|
9 |
|
} |
110
|
|
|
|
111
|
|
|
// Многоитерационный разбор однострчных комментариев |
112
|
19 |
|
while (true) { |
113
|
19 |
|
if (preg_match_all('#--\*(\w+)(.+)#', $this->sql, $matches)) { |
114
|
10 |
|
$count = count($matches[0]); |
115
|
10 |
|
for ($i = 0; $i < $count; $i++) { |
116
|
10 |
|
$this->replaceComment($matches[0][$i], $matches[2][$i], $matches[1][$i]); |
117
|
10 |
|
} |
118
|
10 |
|
} else { |
119
|
19 |
|
break; |
120
|
|
|
} |
121
|
10 |
|
} |
122
|
|
|
|
123
|
19 |
|
$this->sql = preg_replace("/\n+/", "\n", trim($this->sql)); |
124
|
19 |
|
} |
125
|
|
|
|
126
|
|
|
/** |
127
|
|
|
* Заменяем коментарий в запросе на соответствующе преобразованный блок или удаляем. |
128
|
|
|
* @param string $comment Заменямый комментарий. |
129
|
|
|
* @param string $queryInComment Текст внутри комментария. |
130
|
|
|
* @param string $paramName Имя параметра. |
131
|
|
|
*/ |
132
|
10 |
|
private function replaceComment($comment, $queryInComment, $paramName) |
133
|
|
|
{ |
134
|
10 |
|
$param = $this->getParam($paramName); |
135
|
10 |
|
if ($param) { |
136
|
9 |
|
$paramName = $param[0]; |
137
|
9 |
|
$paramValue = $param[1]; |
138
|
9 |
|
if (is_array($paramValue)) { |
139
|
3 |
|
$value = isset($paramValue[0]) ? $paramValue[0] : null; |
140
|
3 |
|
$bind = isset($paramValue['bind']) ? $paramValue['bind'] : true; |
141
|
3 |
|
} else { |
142
|
7 |
|
$value = $paramValue; |
143
|
7 |
|
$bind = true; |
144
|
|
|
} |
145
|
|
|
|
146
|
9 |
|
if ($bind === true && is_array($value)) { |
147
|
1 |
|
$valArr = []; |
148
|
1 |
|
foreach (array_keys($value) as $keyVal) { |
149
|
1 |
|
$valArr[] = ':' . $paramName . '_' . $keyVal; |
150
|
1 |
|
} |
151
|
1 |
|
$replacement = implode(',', $valArr); |
152
|
1 |
|
$queryInComment = preg_replace('/:@' . preg_quote($paramName) . '/', $replacement, $queryInComment); |
153
|
9 |
|
} elseif ($bind === 'text') { |
154
|
1 |
|
$queryInComment = preg_replace('/' . preg_quote($paramName) . '/', $value, $queryInComment); |
155
|
1 |
|
} |
156
|
9 |
|
} else { |
157
|
9 |
|
$queryInComment = ''; |
158
|
|
|
} |
159
|
|
|
|
160
|
10 |
|
$this->sql = str_replace($comment, $queryInComment, $this->sql); |
161
|
10 |
|
} |
162
|
|
|
|
163
|
|
|
/** |
164
|
|
|
* Ищет параметр в массиве $this->params |
165
|
|
|
* @param string $name имя параметра |
166
|
|
|
* @return array|bool массив ['имя_параметра_без_ведущего_двоеточия', 'значение_параметра'] или ложь если параметра нет |
167
|
|
|
*/ |
168
|
10 |
|
private function getParam($name) |
169
|
|
|
{ |
170
|
10 |
|
$name = ltrim($name, ':'); |
171
|
|
|
|
172
|
10 |
|
if (array_key_exists($name, $this->params)) { |
173
|
8 |
|
return [$name, $this->params[$name]]; |
174
|
9 |
|
} elseif (array_key_exists(':' . $name, $this->params)) { |
175
|
1 |
|
return [$name, $this->params[':' . $name]]; |
176
|
|
|
} |
177
|
|
|
|
178
|
9 |
|
return false; |
179
|
|
|
} |
180
|
|
|
} |
181
|
|
|
|