Skip to content

Commit f52f9fb

Browse files
arkanovasarkanovas
andauthored
Fix no space comment 1.5 (#4)
1. Фикс запросов с многострочными комментариями без пробелов - захватывает лишний текст/код 2. Фикс построения запроса при использовании биндинга с типом "text" - если забыли подставить значение, подставляем NULL. 3. Немного форматирования. --------- Co-authored-by: arkanovas <[email protected]>
1 parent 9652f91 commit f52f9fb

File tree

1 file changed

+72
-28
lines changed

1 file changed

+72
-28
lines changed

Parser.php

Lines changed: 72 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,25 @@
11
<?php
2+
23
namespace Intersvyaz\SqlParser;
34

5+
/**
6+
* Разбор текста запроса.
7+
* Получение текста по имени файла sql, парсинг специальных комментариев с именами переменных вида
8+
* /*name AND t.name = :name...
9+
* или
10+
* --*name AND t.name = :name
11+
* Замена комментариев-массивов на строку переменных с уникальными именами:
12+
* --*names AND t.name IN (:@names)
13+
* =>
14+
* AND t.name IN (:names_1, :names_2, :names_3, ...)
15+
*/
416
class Parser
517
{
6-
7-
/**
8-
* @var string Текст sql запроса, который надо преобразовать
9-
*/
18+
/** @var string Текст sql запроса, который надо преобразовать, либо имя файла с запросом */
1019
private $sql;
11-
/**
12-
* @var array параметры, влияющие на парсинг sql запроса
13-
*/
20+
/** @var array Параметры, влияющие на парсинг sql запроса */
1421
private $params;
15-
/**
16-
* @var array "упрощённый" список параметров, для кеширования
17-
*/
22+
/** @var array "Упрощённый" список параметров (для кеширования) */
1823
private $simplifiedParams;
1924

2025
/**
@@ -34,7 +39,8 @@ public function __construct($sql, $params = [])
3439
}
3540

3641
/**
37-
* @return string готовый sql запрос
42+
* Готовый sql запрос
43+
* @return string
3844
*/
3945
public function getSql()
4046
{
@@ -50,7 +56,8 @@ public function __toString()
5056
}
5157

5258
/**
53-
* @return array "упрощённый" список параметров
59+
* "Упрощённый" список параметров
60+
* @return array
5461
*/
5562
public function getSimplifiedParams()
5663
{
@@ -66,7 +73,7 @@ public function getSimplifiedParams()
6673
* @param array $params Параметры построения запроса.
6774
* @return array
6875
*/
69-
private function simplifyParams($params)
76+
private function simplifyParams(array $params)
7077
{
7178
if (empty($params)) {
7279
return $params;
@@ -75,6 +82,7 @@ private function simplifyParams($params)
7582
$newParams = [];
7683
foreach ($params as $key => $value) {
7784
$key = ':' . ltrim($key, ':');
85+
7886
if (is_array($value)) {
7987
if (!isset($value['bind'])) {
8088
$value['bind'] = true;
@@ -93,9 +101,8 @@ private function simplifyParams($params)
93101
}
94102
}
95103
} elseif ($value['bind'] === 'tuple') {
96-
97104
if (isset($value[0]) && is_array($value[0])) {
98-
//скинем индексы
105+
// Скинем индексы
99106
$value[0] = array_values($value[0]);
100107

101108
foreach ($value[0] as $valKey => $valVal) {
@@ -127,9 +134,17 @@ private function simplifyParams($params)
127134
*/
128135
private function parseSql()
129136
{
137+
$matches = null;
138+
130139
// Разбор многострочных комментариев
131-
if (preg_match_all('#/\*([\w|]+)(.+?)\*/#s', $this->sql, $matches)) {
140+
// ВАЖНО: "(.*?)" а не "(.+?)" на случай, если просто написали код такого типа:
141+
// WHEN 700 /*float4*/ THEN 24 /*FLT_MANT_DIG*/
142+
// В случае "+" как комментарий будет распознана строка, включающая код:
143+
// "*/ THEN 24 /*FLT_MANT_DIG"
144+
// который в итоге пропадет потом из результирующей строки запроса.
145+
if (preg_match_all('#/\*([\w|]+)(.*?)\*/#s', $this->sql, $matches)) {
132146
$count = count($matches[0]);
147+
133148
for ($i = 0; $i < $count; $i++) {
134149
$this->replaceComment($matches[0][$i], $matches[2][$i], $matches[1][$i]);
135150
}
@@ -139,6 +154,7 @@ private function parseSql()
139154
while (true) {
140155
if (preg_match_all('#--\*([\w|]+)(.+)#', $this->sql, $matches)) {
141156
$count = count($matches[0]);
157+
142158
for ($i = 0; $i < $count; $i++) {
143159
$this->replaceComment($matches[0][$i], $matches[2][$i], $matches[1][$i]);
144160
}
@@ -147,9 +163,10 @@ private function parseSql()
147163
}
148164
}
149165

150-
// разбор переменных - массивов, которые находились изначально вне комментариев
166+
// Разбор переменных-массивов, которые находились изначально вне комментариев
151167
if (preg_match_all('#:@(\w+)#', $this->sql, $matches)) {
152168
$count = count($matches[0]);
169+
153170
for ($i = 0; $i < $count; $i++) {
154171
$this->replaceComment($matches[0][$i], $matches[0][$i], $matches[1][$i], false);
155172
}
@@ -159,15 +176,15 @@ private function parseSql()
159176
}
160177

161178
/**
162-
* Заменяем коментарий или некоторую другую подстроку в запросе на соответствующе преобразованный блок или удаляем,
179+
* Заменяем комментарий или некоторую другую подстроку в запросе на соответствующе преобразованный блок или удаляем,
163180
* если указан соответствующий параметр (делается по умолчанию - для комментариев).
164-
* Используется также для замены параметра-массива - :@<param_name> не помещенного в комментарий, но только если такой
165-
* параметр есть в массиве параметров. Отдельную функцию делать не стали, потому что функционал одинаковый.
181+
* Используется также для замены параметра-массива - :@<param_name> не помещенного в комментарий, но только если
182+
* такой параметр есть в массиве параметров. Отдельную функцию делать не стали, потому что функционал одинаковый.
166183
* Либо можно переименовать функцию.
167-
* @param string $comment Заменямый комментарий.
184+
* @param string $comment Заменяемый комментарий.
168185
* @param string $queryInComment Текст внутри комментария.
169186
* @param string $paramName Имя параметра.
170-
* @param boolean $replaceNotFoundParam заменять ли комментарий, если не нашли соответствующего параметра в списке
187+
* @param boolean $replaceNotFoundParam Заменять ли комментарий, если не нашли соответствующего параметра в списке
171188
*/
172189
private function replaceComment($comment, $queryInComment, $paramName, $replaceNotFoundParam = true)
173190
{
@@ -189,46 +206,72 @@ private function replaceComment($comment, $queryInComment, $paramName, $replaceN
189206
} elseif ($param) {
190207
$paramName = $param[0];
191208
$paramValue = $param[1];
192-
if (is_array($paramValue)) {
209+
210+
if (is_array($paramValue) && array_key_exists('bind', $paramValue)) {
211+
/** Пришла пара bind/value, либо просто один bind (тогда он задается = false). */
212+
$bind = $paramValue['bind'];
213+
214+
if ($paramValue['bind'] !== false) {
215+
/**
216+
* Если указано, что надо что-то биндить, то "value" обязателен.
217+
* Но в некоторых местах его используют неправильно для "bind" => "text".
218+
* По-правильному надо было бы там использовать "bind" => false.
219+
*/
220+
$value = isset($paramValue['value']) ? $paramValue['value'] : null;
221+
}
222+
} elseif (is_array($paramValue)) {
223+
/**
224+
* Значение параметра - это массив, но почему-то без элемента "bind".
225+
* Это случай, когда у нас элементы для списка значений IN.
226+
* Там массив значений внутри массива - т.е. первым элементом массива является массив значений
227+
* "связанной" переменной.
228+
*/
193229
$value = isset($paramValue[0]) ? $paramValue[0] : null;
194-
$bind = isset($paramValue['bind']) ? $paramValue['bind'] : true;
230+
$bind = true;
195231
} else {
196232
$value = $paramValue;
197233
$bind = true;
198234
}
199235

200236
if ($bind === true && is_array($value)) {
201237
$valArr = [];
238+
202239
foreach (array_keys($value) as $keyVal) {
203240
$valArr[] = ':' . $paramName . '_' . $keyVal;
204241
}
242+
205243
$replacement = implode(',', $valArr);
206244
$queryInComment = preg_replace('/:@' . preg_quote($paramName) . '/i', $replacement, $queryInComment);
207245
} elseif ($bind === 'text') {
208246
$queryInComment = preg_replace('/' . preg_quote($paramName) . '/i', $value, $queryInComment);
209247
} elseif ($bind === 'tuple') {
210248
if (is_array($paramValue[0])) {
211249
$replacements = [];
212-
//скинем индексы
250+
// Скинем индексы
213251
$paramValue[0] = array_values($paramValue[0]);
214252

215253
foreach ($paramValue[0] as $keyParam => $val) {
216254
$name = ':' . $paramName . '_' . $keyParam;
217255
if (is_array($val)) {
218256
$valArr = [];
257+
219258
foreach (array_keys($val) as $keyVal) {
220259
$valArr[] = $name . '_' . $keyVal;
221260
}
261+
222262
$valName = implode(',', $valArr);
223263
} else {
224264
$valName = $name;
225265
}
266+
226267
$replacements[] = '(' . $valName . ')';
227268
}
269+
228270
$replacement = implode(',', $replacements);
229271
} else {
230272
$replacement = $paramValue;
231273
}
274+
232275
$queryInComment = preg_replace('/:@' . preg_quote($paramName) . '/i', $replacement, $queryInComment);
233276
}
234277
} elseif ($replaceNotFoundParam) {
@@ -240,9 +283,9 @@ private function replaceComment($comment, $queryInComment, $paramName, $replaceN
240283

241284
/**
242285
* Ищет параметр в массиве $this->params
243-
* @param string $name имя параметра
244-
* @return array|bool массив ['имя_параметра_без_ведущего_двоеточия', 'значение_параметра'] или ложь если параметра
245-
* нет
286+
* @param string $name Имя параметра
287+
* @return array|bool Массив ['имя_параметра_без_ведущего_двоеточия', 'значение_параметра']
288+
* или ложь, если параметра нет
246289
*/
247290
private function getParam($name)
248291
{
@@ -251,6 +294,7 @@ private function getParam($name)
251294
// Формируем имя параметра на выход точно такое же, какое и забиндено в параметры.
252295
foreach ($this->params as $key => $value) {
253296
$key = ltrim($key, ':');
297+
254298
if (mb_strtolower($key) == $name) {
255299
$outName = $key;
256300
break;

0 commit comments

Comments
 (0)