From c1e94ef0237a0f340a42576a58d4e7325e636e3c Mon Sep 17 00:00:00 2001 From: arkanovas Date: Fri, 17 Oct 2025 14:47:32 +0500 Subject: [PATCH 1/2] =?UTF-8?q?=D0=A4=D0=B8=D0=BA=D1=81=20=D0=B7=D0=B0?= =?UTF-8?q?=D0=BF=D1=80=D0=BE=D1=81=D0=BE=D0=B2=20=D1=81=20=D0=BC=D0=BD?= =?UTF-8?q?=D0=BE=D0=B3=D0=BE=D1=81=D1=82=D1=80=D0=BE=D1=87=D0=BD=D1=8B?= =?UTF-8?q?=D0=BC=D0=B8=20=D0=BA=D0=BE=D0=BC=D0=BC=D0=B5=D0=BD=D1=82=D0=B0?= =?UTF-8?q?=D1=80=D0=B8=D1=8F=D0=BC=D0=B8=20=D0=B1=D0=B5=D0=B7=20=D0=BF?= =?UTF-8?q?=D1=80=D0=BE=D0=B1=D0=B5=D0=BB=D0=BE=D0=B2=20-=20=D0=B7=D0=B0?= =?UTF-8?q?=D1=85=D0=B2=D0=B0=D1=82=D1=8B=D0=B2=D0=B0=D0=B5=D1=82=20=D0=BB?= =?UTF-8?q?=D0=B8=D1=88=D0=BD=D0=B8=D0=B9=20=D1=82=D0=B5=D0=BA=D1=81=D1=82?= =?UTF-8?q?/=D0=BA=D0=BE=D0=B4=20+=20=D0=BD=D0=B5=D0=BC=D0=BD=D0=BE=D0=B3?= =?UTF-8?q?=D0=BE=20=D1=84=D0=BE=D1=80=D0=BC=D0=B0=D1=82=D0=B8=D1=80=D0=BE?= =?UTF-8?q?=D0=B2=D0=B0=D0=BD=D0=B8=D1=8F.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Parser.php | 64 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 42 insertions(+), 22 deletions(-) diff --git a/Parser.php b/Parser.php index 382926c..55f0b1a 100644 --- a/Parser.php +++ b/Parser.php @@ -1,20 +1,19 @@ $value) { $key = ':' . ltrim($key, ':'); + if (is_array($value)) { if (!isset($value['bind'])) { $value['bind'] = true; @@ -93,9 +95,8 @@ private function simplifyParams($params) } } } elseif ($value['bind'] === 'tuple') { - if (isset($value[0]) && is_array($value[0])) { - //скинем индексы + // Скинем индексы $value[0] = array_values($value[0]); foreach ($value[0] as $valKey => $valVal) { @@ -127,9 +128,17 @@ private function simplifyParams($params) */ private function parseSql() { + $matches = null; + // Разбор многострочных комментариев - if (preg_match_all('#/\*([\w|]+)(.+?)\*/#s', $this->sql, $matches)) { + // ВАЖНО: "(.*?)" а не "(.+?)" на случай, если просто написали код такого типа: + // WHEN 700 /*float4*/ THEN 24 /*FLT_MANT_DIG*/ + // В случае "+" как комментарий будет распознана строка, включающая код: + // "*/ THEN 24 /*FLT_MANT_DIG" + // который в итоге пропадет потом из результирующей строки запроса. + if (preg_match_all('#/\*([\w|]+)(.*?)\*/#s', $this->sql, $matches)) { $count = count($matches[0]); + for ($i = 0; $i < $count; $i++) { $this->replaceComment($matches[0][$i], $matches[2][$i], $matches[1][$i]); } @@ -139,6 +148,7 @@ private function parseSql() while (true) { if (preg_match_all('#--\*([\w|]+)(.+)#', $this->sql, $matches)) { $count = count($matches[0]); + for ($i = 0; $i < $count; $i++) { $this->replaceComment($matches[0][$i], $matches[2][$i], $matches[1][$i]); } @@ -147,9 +157,10 @@ private function parseSql() } } - // разбор переменных - массивов, которые находились изначально вне комментариев + // Разбор переменных-массивов, которые находились изначально вне комментариев if (preg_match_all('#:@(\w+)#', $this->sql, $matches)) { $count = count($matches[0]); + for ($i = 0; $i < $count; $i++) { $this->replaceComment($matches[0][$i], $matches[0][$i], $matches[1][$i], false); } @@ -159,10 +170,10 @@ private function parseSql() } /** - * Заменяем коментарий или некоторую другую подстроку в запросе на соответствующе преобразованный блок или удаляем, + * Заменяем комментарий или некоторую другую подстроку в запросе на соответствующе преобразованный блок или удаляем, * если указан соответствующий параметр (делается по умолчанию - для комментариев). - * Используется также для замены параметра-массива - :@ не помещенного в комментарий, но только если такой - * параметр есть в массиве параметров. Отдельную функцию делать не стали, потому что функционал одинаковый. + * Используется также для замены параметра-массива - :@ не помещенного в комментарий, но только если + * такой параметр есть в массиве параметров. Отдельную функцию делать не стали, потому что функционал одинаковый. * Либо можно переименовать функцию. * @param string $comment Заменямый комментарий. * @param string $queryInComment Текст внутри комментария. @@ -189,6 +200,7 @@ private function replaceComment($comment, $queryInComment, $paramName, $replaceN } elseif ($param) { $paramName = $param[0]; $paramValue = $param[1]; + if (is_array($paramValue)) { $value = isset($paramValue[0]) ? $paramValue[0] : null; $bind = isset($paramValue['bind']) ? $paramValue['bind'] : true; @@ -199,9 +211,11 @@ private function replaceComment($comment, $queryInComment, $paramName, $replaceN if ($bind === true && is_array($value)) { $valArr = []; + foreach (array_keys($value) as $keyVal) { $valArr[] = ':' . $paramName . '_' . $keyVal; } + $replacement = implode(',', $valArr); $queryInComment = preg_replace('/:@' . preg_quote($paramName) . '/i', $replacement, $queryInComment); } elseif ($bind === 'text') { @@ -209,26 +223,31 @@ private function replaceComment($comment, $queryInComment, $paramName, $replaceN } elseif ($bind === 'tuple') { if (is_array($paramValue[0])) { $replacements = []; - //скинем индексы + // Скинем индексы $paramValue[0] = array_values($paramValue[0]); foreach ($paramValue[0] as $keyParam => $val) { $name = ':' . $paramName . '_' . $keyParam; if (is_array($val)) { $valArr = []; + foreach (array_keys($val) as $keyVal) { $valArr[] = $name . '_' . $keyVal; } + $valName = implode(',', $valArr); } else { $valName = $name; } + $replacements[] = '(' . $valName . ')'; } + $replacement = implode(',', $replacements); } else { $replacement = $paramValue; } + $queryInComment = preg_replace('/:@' . preg_quote($paramName) . '/i', $replacement, $queryInComment); } } elseif ($replaceNotFoundParam) { @@ -241,7 +260,7 @@ private function replaceComment($comment, $queryInComment, $paramName, $replaceN /** * Ищет параметр в массиве $this->params * @param string $name имя параметра - * @return array|bool массив ['имя_параметра_без_ведущего_двоеточия', 'значение_параметра'] или ложь если параметра + * @return array|bool массив ['имя_параметра_без_ведущего_двоеточия', 'значение_параметра'] или ложь, если параметра * нет */ private function getParam($name) @@ -251,6 +270,7 @@ private function getParam($name) // Формируем имя параметра на выход точно такое же, какое и забиндено в параметры. foreach ($this->params as $key => $value) { $key = ltrim($key, ':'); + if (mb_strtolower($key) == $name) { $outName = $key; break; From d3c1401e231561f67a2ad2aaa446f37c416f46d6 Mon Sep 17 00:00:00 2001 From: arkanovas Date: Fri, 17 Oct 2025 15:52:38 +0500 Subject: [PATCH 2/2] =?UTF-8?q?1.=20=D0=A4=D0=B8=D0=BA=D1=81=20=D0=B7?= =?UTF-8?q?=D0=B0=D0=BF=D1=80=D0=BE=D1=81=D0=BE=D0=B2=20=D1=81=20=D0=BC?= =?UTF-8?q?=D0=BD=D0=BE=D0=B3=D0=BE=D1=81=D1=82=D1=80=D0=BE=D1=87=D0=BD?= =?UTF-8?q?=D1=8B=D0=BC=D0=B8=20=D0=BA=D0=BE=D0=BC=D0=BC=D0=B5=D0=BD=D1=82?= =?UTF-8?q?=D0=B0=D1=80=D0=B8=D1=8F=D0=BC=D0=B8=20=D0=B1=D0=B5=D0=B7=20?= =?UTF-8?q?=D0=BF=D1=80=D0=BE=D0=B1=D0=B5=D0=BB=D0=BE=D0=B2=20-=20=D0=B7?= =?UTF-8?q?=D0=B0=D1=85=D0=B2=D0=B0=D1=82=D1=8B=D0=B2=D0=B0=D0=B5=D1=82=20?= =?UTF-8?q?=D0=BB=D0=B8=D1=88=D0=BD=D0=B8=D0=B9=20=D1=82=D0=B5=D0=BA=D1=81?= =?UTF-8?q?=D1=82/=D0=BA=D0=BE=D0=B4=202.=20=D0=A4=D0=B8=D0=BA=D1=81=20?= =?UTF-8?q?=D0=BF=D0=BE=D1=81=D1=82=D1=80=D0=BE=D0=B5=D0=BD=D0=B8=D1=8F=20?= =?UTF-8?q?=D0=B7=D0=B0=D0=BF=D1=80=D0=BE=D1=81=D0=B0=20=D0=BF=D1=80=D0=B8?= =?UTF-8?q?=20=D0=B8=D1=81=D0=BF=D0=BE=D0=BB=D1=8C=D0=B7=D0=BE=D0=B2=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D0=B8=20=D0=B1=D0=B8=D0=BD=D0=B4=D0=B8=D0=BD=D0=B3?= =?UTF-8?q?=D0=B0=20=D1=81=20=D1=82=D0=B8=D0=BF=D0=BE=D0=BC=20"text"=20-?= =?UTF-8?q?=20=D0=B5=D1=81=D0=BB=D0=B8=20=D0=B7=D0=B0=D0=B1=D1=8B=D0=BB?= =?UTF-8?q?=D0=B8=20=D0=BF=D0=BE=D0=B4=D1=81=D1=82=D0=B0=D0=B2=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20=D0=B7=D0=BD=D0=B0=D1=87=D0=B5=D0=BD=D0=B8=D0=B5,=20?= =?UTF-8?q?=D0=BF=D0=BE=D0=B4=D1=81=D1=82=D0=B0=D0=B2=D0=BB=D1=8F=D0=B5?= =?UTF-8?q?=D0=BC=20NULL.=203.=20=D0=9D=D0=B5=D0=BC=D0=BD=D0=BE=D0=B3?= =?UTF-8?q?=D0=BE=20=D1=84=D0=BE=D1=80=D0=BC=D0=B0=D1=82=D0=B8=D1=80=D0=BE?= =?UTF-8?q?=D0=B2=D0=B0=D0=BD=D0=B8=D1=8F.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Parser.php | 42 +++++++++++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/Parser.php b/Parser.php index 55f0b1a..718d8fa 100644 --- a/Parser.php +++ b/Parser.php @@ -4,8 +4,14 @@ /** * Разбор текста запроса. - * Получение текста по имени файла sql, парсинг специальных комментариев с именами переменных, - * замена комментариев-массивов на строку переменных с уникальными именами + * Получение текста по имени файла sql, парсинг специальных комментариев с именами переменных вида + * /*name AND t.name = :name... + * или + * --*name AND t.name = :name + * Замена комментариев-массивов на строку переменных с уникальными именами: + * --*names AND t.name IN (:@names) + * => + * AND t.name IN (:names_1, :names_2, :names_3, ...) */ class Parser { @@ -175,10 +181,10 @@ private function parseSql() * Используется также для замены параметра-массива - :@ не помещенного в комментарий, но только если * такой параметр есть в массиве параметров. Отдельную функцию делать не стали, потому что функционал одинаковый. * Либо можно переименовать функцию. - * @param string $comment Заменямый комментарий. + * @param string $comment Заменяемый комментарий. * @param string $queryInComment Текст внутри комментария. * @param string $paramName Имя параметра. - * @param boolean $replaceNotFoundParam заменять ли комментарий, если не нашли соответствующего параметра в списке + * @param boolean $replaceNotFoundParam Заменять ли комментарий, если не нашли соответствующего параметра в списке */ private function replaceComment($comment, $queryInComment, $paramName, $replaceNotFoundParam = true) { @@ -201,9 +207,27 @@ private function replaceComment($comment, $queryInComment, $paramName, $replaceN $paramName = $param[0]; $paramValue = $param[1]; - if (is_array($paramValue)) { + if (is_array($paramValue) && array_key_exists('bind', $paramValue)) { + /** Пришла пара bind/value, либо просто один bind (тогда он задается = false). */ + $bind = $paramValue['bind']; + + if ($paramValue['bind'] !== false) { + /** + * Если указано, что надо что-то биндить, то "value" обязателен. + * Но в некоторых местах его используют неправильно для "bind" => "text". + * По-правильному надо было бы там использовать "bind" => false. + */ + $value = isset($paramValue['value']) ? $paramValue['value'] : null; + } + } elseif (is_array($paramValue)) { + /** + * Значение параметра - это массив, но почему-то без элемента "bind". + * Это случай, когда у нас элементы для списка значений IN. + * Там массив значений внутри массива - т.е. первым элементом массива является массив значений + * "связанной" переменной. + */ $value = isset($paramValue[0]) ? $paramValue[0] : null; - $bind = isset($paramValue['bind']) ? $paramValue['bind'] : true; + $bind = true; } else { $value = $paramValue; $bind = true; @@ -259,9 +283,9 @@ private function replaceComment($comment, $queryInComment, $paramName, $replaceN /** * Ищет параметр в массиве $this->params - * @param string $name имя параметра - * @return array|bool массив ['имя_параметра_без_ведущего_двоеточия', 'значение_параметра'] или ложь, если параметра - * нет + * @param string $name Имя параметра + * @return array|bool Массив ['имя_параметра_без_ведущего_двоеточия', 'значение_параметра'] + * или ложь, если параметра нет */ private function getParam($name) {