1
+ <?php
2
+
3
+
4
+ namespace DigitalStars \DataBase ;
5
+
6
+
7
+ trait Parser {
8
+ private $ args ;
9
+ private $ original_query ;
10
+ private $ is_inner = false ;
11
+
12
+ public function parse ($ query , array $ args ) {
13
+ $ this ->is_inner = false ;
14
+ $ this ->original_query = $ query ;
15
+ $ this ->args = $ args ;
16
+ $ anon = function ($ v ) {
17
+ return $ this ->parse_part ($ v );
18
+ };
19
+ return preg_replace_callback ("!(\?[avw]\[.*?\])|(\?[avw]?[sidnf])!i " , $ anon , $ query );
20
+ }
21
+
22
+ private function parse_part ($ symbol , $ value = null ) {
23
+ if (is_array ($ symbol ))
24
+ $ symbol = $ symbol [0 ];
25
+ if ($ symbol == '? ' )
26
+ return $ symbol ;
27
+ $ symbol = trim ($ symbol );
28
+ if (!$ this ->is_inner and $ symbol != '?n ' )
29
+ if (count ($ this ->args ) == 0 )
30
+ throw new Exception ('Несовпадение количества аргументов и заполнителей в массиве, запрос ' . $ this ->original_query );
31
+ else
32
+ $ value = array_shift ($ this ->args );
33
+ switch ($ symbol [1 ]) {
34
+ case "S " :
35
+ $ is_like_escaping = true ;
36
+ case 's ' :
37
+ $ value = $ this ->getValueStringType ($ value );
38
+ return isset ($ is_like_escaping ) ? $ this ->escapeLike ($ value ) : $ this ->realEscapeString ($ value );
39
+ case 'i ' :
40
+ return $ this ->getValueIntType ($ value );
41
+ case 'd ' :
42
+ return $ this ->getValueFloatType ($ value );
43
+ case 'n ' :
44
+ return "NULL " ;
45
+ case 'f ' :
46
+ return $ this ->escapeFieldName ($ value );
47
+ case 'A ' :
48
+ $ is_associative_array = true ;
49
+ case 'a ' :
50
+ $ value = $ this ->parseArray ($ symbol , $ value );
51
+ if (isset ($ is_associative_array )) {
52
+ foreach ($ value as $ key => $ val )
53
+ $ result [] = $ this ->escapeFieldName ($ key ) . "= " . $ val ;
54
+ return join (', ' , $ result );
55
+ } else
56
+ return join (', ' , $ value );
57
+ case "v " :
58
+ if (!is_array ($ value ))
59
+ throw new Exception ($ this ->createErrorMessage ('array ' , $ value ));
60
+ foreach ($ value as $ key => $ val )
61
+ $ value [$ key ] = join (', ' , $ this ->parseArray ($ symbol , $ value [$ key ]));
62
+ return "( " .join ("),( " , $ value ).") " ;
63
+ case "w " :
64
+ $ value = $ this ->parseArray ($ symbol , $ value );
65
+ foreach ($ value as $ key => $ val )
66
+ $ result [] = $ this ->escapeFieldName ($ key ) . "= " . $ val ;
67
+ return join (' AND ' , $ result );
68
+ default :
69
+ throw new Exception ("Неизвестный заполнитель {$ symbol [2 ]}" );
70
+
71
+ }
72
+ }
73
+
74
+ private function parseArray ($ symbol , $ value ) {
75
+ if (!is_array ($ value ))
76
+ throw new Exception ($ this ->createErrorMessage ('array ' , $ value ));
77
+ if ($ symbol [2 ] == 'i ' )
78
+ foreach ($ value as $ key => $ val )
79
+ $ value [$ key ] = $ this ->getValueIntType ($ val );
80
+ else if ($ symbol [2 ] == 'd ' )
81
+ foreach ($ value as $ key => $ val )
82
+ $ value [$ key ] = $ this ->getValueFloatType ($ val );
83
+ else if ($ symbol [2 ] == 's ' )
84
+ foreach ($ value as $ key => $ val )
85
+ $ value [$ key ] = $ this ->realEscapeString ($ this ->getValueStringType ($ val ));
86
+ else if ($ symbol [2 ] == 'f ' )
87
+ foreach ($ value as $ key => $ val )
88
+ $ value [$ key ] = $ this ->escapeFieldName ($ val );
89
+ else if ($ symbol [2 ] == '[ ' ) {
90
+ $ selectors = explode (', ' ,
91
+ substr ($ symbol , 3 , strlen ($ symbol ) - 4 ));
92
+ if (count ($ selectors ) != count ($ value ))
93
+ throw new Exception ('Несовпадение количества аргументов и заполнителей в массиве, запрос ' . $ this ->original_query );
94
+ $ this ->is_inner = true ;
95
+ $ index = 0 ;
96
+ foreach ($ value as $ key => $ val )
97
+ $ value [$ key ] = $ this ->parse_part ($ selectors [$ index ++], $ val );
98
+ $ this ->is_inner = false ;
99
+ } else
100
+ throw new Exception ("Неизвестный заполнитель {$ symbol [2 ]}" );
101
+ return $ value ;
102
+ }
103
+
104
+ private function escapeFieldName ($ value ) {
105
+ if (!is_string ($ value )) {
106
+ throw new Exception ($ this ->createErrorMessage ('field ' , $ value ));
107
+ }
108
+
109
+ $ new_value = '' ;
110
+
111
+ $ replace = function ($ value ) {
112
+ return '` ' . str_replace ("` " , "`` " , $ value ) . '` ' ;
113
+ };
114
+
115
+ $ dot = false ;
116
+
117
+ if ($ values = explode ('. ' , $ value )) {
118
+ foreach ($ values as $ value ) {
119
+ if ($ value === '' ) {
120
+ if (!$ dot ) {
121
+ $ dot = true ;
122
+ $ new_value .= '. ' ;
123
+ } else {
124
+ throw new Exception ('Два символа `.` идущие подряд в имени столбца или таблицы ' );
125
+ }
126
+ } else {
127
+ $ new_value .= $ replace ($ value ) . '. ' ;
128
+ }
129
+ }
130
+
131
+ return rtrim ($ new_value , '. ' );
132
+ } else {
133
+ return $ replace ($ value );
134
+ }
135
+ }
136
+
137
+ private function getValueIntType ($ value ) {
138
+ if (is_integer ($ value ))
139
+ return $ value ;
140
+ if (is_numeric ($ value ) || is_null ($ value ) || is_bool ($ value )) {
141
+ return (int )$ value ;
142
+ }
143
+ throw new Exception ($ this ->createErrorMessage ('integer ' , $ value ));
144
+ }
145
+
146
+ private function getValueFloatType ($ value ) {
147
+ if (is_float ($ value )) {
148
+ return $ value ;
149
+ }
150
+ if (is_numeric ($ value ) || is_null ($ value ) || is_bool ($ value )) {
151
+ return (float )$ value ;
152
+ }
153
+ throw new Exception ($ this ->createErrorMessage ('double ' , $ value ));
154
+ }
155
+
156
+ private function getValueStringType ($ value ) {
157
+ // меняем поведение PHP в отношении приведения bool к string
158
+ if (is_bool ($ value )) {
159
+ return (string )(int )$ value ;
160
+ }
161
+
162
+ if (!is_string ($ value ) && !(is_numeric ($ value ) || is_null ($ value ))) {
163
+ throw new Exception ($ this ->createErrorMessage ('string ' , $ value ));
164
+ }
165
+
166
+ return (string )$ value ;
167
+ }
168
+
169
+ private function escapeLike ($ var , $ chars = "%_ " ) {
170
+ $ var = str_replace ('\\' , '\\\\' , $ var );
171
+ $ var = $ this ->realEscapeString ($ var );
172
+
173
+ if ($ chars ) {
174
+ $ var = addCslashes ($ var , $ chars );
175
+ }
176
+
177
+ return $ var ;
178
+ }
179
+
180
+ private function realEscapeString ($ value ) {
181
+ return $ this ->quote ($ value );
182
+ }
183
+
184
+ private function createErrorMessage ($ type , $ value ) {
185
+ return "Попытка указать для заполнителя типа $ type значение типа " . gettype ($ value ) . " в шаблоне запроса $ this ->original_query " ;
186
+ }
187
+ }
0 commit comments