1717#include " php.h"
1818#include " php_pdo_driver.h"
1919#include " php_pdo_int.h"
20+ #include " pdo_sql_parser.h"
2021
21- #define PDO_PARSER_TEXT 1
22- #define PDO_PARSER_BIND 2
23- #define PDO_PARSER_BIND_POS 3
24- #define PDO_PARSER_ESCAPED_QUESTION 4
25- #define PDO_PARSER_EOI 5
26-
27- #define PDO_PARSER_BINDNO_ESCAPED_CHAR -1
28-
29- #define RET (i ) {s->cur = cursor; return i; }
30- #define SKIP_ONE (i ) {s->cur = s->tok + 1 ; return i; }
31-
32- #define YYCTYPE unsigned char
33- #define YYCURSOR cursor
34- #define YYLIMIT s->end
35- #define YYMARKER s->ptr
36- #define YYFILL (n ) { RET (PDO_PARSER_EOI); }
37-
38- typedef struct Scanner {
39- const char *ptr, *cur, *tok, *end;
40- } Scanner;
41-
42- static int scan (Scanner *s)
22+ static int default_scanner (pdo_scanner_t *s)
4323{
4424 const char *cursor = s->cur ;
4525
4626 s->tok = cursor;
4727 /* !re2c
4828 BINDCHR = [:][a-zA-Z0-9_]+;
4929 QUESTION = [?];
50- ESCQUESTION = [?][?];
51- COMMENTS = ("/*"([^*]+|[*]+[^/*])*[*]*"*/ " |" --" [^\r\n ]*);
52- SPECIALS = [:?" ' -/];
53- MULTICHAR = [:]{2,};
30+ COMMENTS = ("/*"([^*]+|[*]+[^/*])*[*]*"*/ " |" --" .*);
31+ SPECIALS = [:?" ' /-];
32+ MULTICHAR = ([:]{2,}|[?]{2,});
5433 ANYNOEOF = [\001 -\377 ];
5534 */
5635
5736 /*!re2c
58- (["](([\\ ]ANYNOEOF )|ANYNOEOF\[ "\\ ])*["]) { RET(PDO_PARSER_TEXT); }
59- ([' ](([\\]ANYNOEOF )|ANYNOEOF\[' \\ ])*[' ]) { RET (PDO_PARSER_TEXT); }
37+ (["]((["]["] )|ANYNOEOF\[ "])*["]) { RET(PDO_PARSER_TEXT); }
38+ ([' ](([' ][ ' ] )|ANYNOEOF\[' ])*[' ]) { RET (PDO_PARSER_TEXT); }
6039 MULTICHAR { RET (PDO_PARSER_TEXT); }
61- ESCQUESTION { RET (PDO_PARSER_ESCAPED_QUESTION); }
6240 BINDCHR { RET (PDO_PARSER_BIND); }
6341 QUESTION { RET (PDO_PARSER_BIND_POS); }
6442 SPECIALS { SKIP_ONE (PDO_PARSER_TEXT); }
@@ -75,13 +53,18 @@ struct placeholder {
7553 struct placeholder *next;
7654};
7755
56+ struct custom_quote {
57+ const char *pos;
58+ size_t len;
59+ };
60+
7861static void free_param_name (zval *el) {
7962 zend_string_release (Z_PTR_P (el));
8063}
8164
8265PDO_API int pdo_parse_params (pdo_stmt_t *stmt, zend_string *inquery, zend_string **outquery)
8366{
84- Scanner s;
67+ pdo_scanner_t s;
8568 char *newbuffer;
8669 ptrdiff_t t;
8770 uint32_t bindno = 0 ;
@@ -91,12 +74,42 @@ PDO_API int pdo_parse_params(pdo_stmt_t *stmt, zend_string *inquery, zend_string
9174 struct pdo_bound_param_data *param;
9275 int query_type = PDO_PLACEHOLDER_NONE;
9376 struct placeholder *placeholders = NULL , *placetail = NULL , *plc = NULL ;
77+ int (*scan)(pdo_scanner_t *s);
78+ struct custom_quote custom_quote = {NULL , 0 };
79+
80+ scan = stmt->dbh ->methods ->scanner ? stmt->dbh ->methods ->scanner : default_scanner;
9481
9582 s.cur = ZSTR_VAL (inquery);
9683 s.end = s.cur + ZSTR_LEN (inquery) + 1 ;
9784
9885 /* phase 1: look for args */
9986 while ((t = scan (&s)) != PDO_PARSER_EOI) {
87+ if (custom_quote.pos ) {
88+ /* Inside a custom quote */
89+ if (t == PDO_PARSER_CUSTOM_QUOTE && custom_quote.len == s.cur - s.tok && !strncmp (s.tok , custom_quote.pos , custom_quote.len )) {
90+ /* Matching closing quote found, end custom quoting */
91+ custom_quote.pos = NULL ;
92+ custom_quote.len = 0 ;
93+ } else if (t == PDO_PARSER_ESCAPED_QUESTION) {
94+ /* An escaped question mark has been used inside a dollar quoted string, most likely as a workaround
95+ * as a single "?" would have been parsed as placeholder, due to the lack of support for dollar quoted
96+ * strings. For now, we emit a deprecation notice, but still process it */
97+ php_error_docref (NULL , E_DEPRECATED, " Escaping question marks inside dollar quoted strings is not required anymore and is deprecated" );
98+
99+ goto placeholder;
100+ }
101+
102+ continue ;
103+ }
104+
105+ if (t == PDO_PARSER_CUSTOM_QUOTE) {
106+ /* Start of a custom quote, keep a reference to search for the matching closing quote */
107+ custom_quote.pos = s.tok ;
108+ custom_quote.len = s.cur - s.tok ;
109+
110+ continue ;
111+ }
112+
100113 if (t == PDO_PARSER_BIND || t == PDO_PARSER_BIND_POS || t == PDO_PARSER_ESCAPED_QUESTION) {
101114 if (t == PDO_PARSER_ESCAPED_QUESTION && stmt->supports_placeholders == PDO_PLACEHOLDER_POSITIONAL) {
102115 /* escaped question marks unsupported, treat as text */
@@ -113,6 +126,7 @@ PDO_API int pdo_parse_params(pdo_stmt_t *stmt, zend_string *inquery, zend_string
113126 query_type |= PDO_PLACEHOLDER_POSITIONAL;
114127 }
115128
129+ placeholder:
116130 plc = emalloc (sizeof (*plc));
117131 memset (plc, 0 , sizeof (*plc));
118132 plc->next = NULL ;
0 commit comments