PluginProbe
SQLite Database Integration / 3.1.0
SQLite Database Integration v3.1.0
3.1.0 3.0.2 3.0.1 trunk 2.1.13 2.1.14 2.1.15 2.1.16 2.2.0 2.2.1 2.2.10 2.2.11 2.2.12 2.2.13 2.2.14 2.2.15 2.2.16 2.2.17 2.2.18 2.2.19 2.2.2 2.2.20 2.2.21 2.2.22 2.2.23 All 33 releases
sqlite-database-integration / wp-includes / database / mysql / class-wp-mysql-token.php

class-wp-mysql-token.php in SQLite Database Integration 3.1.0, at wp-includes/database/mysql/class-wp-mysql-token.php

200 lines 7.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * MySQL token.
5 *
6 * This class represents a MySQL SQL token that is produced by WP_MySQL_Lexer,
7 * and consumed by WP_MySQL_Parser during the parsing process.
8 *
9 * @access private
10 */
11 class WP_MySQL_Token extends WP_Parser_Token {
12 /**
13 * Whether the NO_BACKSLASH_ESCAPES SQL mode is enabled.
14 *
15 * @var bool
16 */
17 private $sql_mode_no_backslash_escapes_enabled;
18
19 /**
20 * Constructor.
21 *
22 * @param int $id Token type.
23 * @param int $start Byte offset in the input where the token begins.
24 * @param int $length Byte length of the token in the input.
25 * @param string $input Input bytes from which the token was parsed.
26 * @param bool $sql_mode_no_backslash_escapes_enabled Whether the NO_BACKSLASH_ESCAPES SQL mode is enabled.
27 */
28 public function __construct(
29 int $id,
30 int $start,
31 int $length,
32 string $input,
33 bool $sql_mode_no_backslash_escapes_enabled
34 ) {
35 $this->id = $id;
36 $this->start = $start;
37 $this->length = $length;
38 $this->input = $input;
39
40 $this->sql_mode_no_backslash_escapes_enabled = $sql_mode_no_backslash_escapes_enabled;
41 }
42
43 /**
44 * Get the name of the token.
45 *
46 * This method is intended to be used only for testing and debugging purposes,
47 * when tokens need to be presented by their names in a human-readable form.
48 * It should not be used in production code, as it's not performance-optimized.
49 *
50 * @return string The token name.
51 */
52 public function get_name(): string {
53 $name = WP_MySQL_Lexer::get_token_name( $this->id );
54 if ( null === $name ) {
55 $name = 'UNKNOWN';
56 }
57 return $name;
58 }
59
60 /**
61 * Get the real unquoted value of the token.
62 *
63 * @return string The token value.
64 */
65 public function get_value(): string {
66 $value = $this->get_bytes();
67 if (
68 WP_MySQL_Lexer::SINGLE_QUOTED_TEXT === $this->id
69 || WP_MySQL_Lexer::DOUBLE_QUOTED_TEXT === $this->id
70 || WP_MySQL_Lexer::BACK_TICK_QUOTED_ID === $this->id
71 ) {
72 // Remove bounding quotes.
73 $quote = $value[0];
74 $value = substr( $value, 1, -1 );
75
76 /*
77 * Quoted identifiers escape their bounding quote by doubling it, while
78 * backslashes remain literal. Strings in NO_BACKSLASH_ESCAPES mode use
79 * the same unquoting rule.
80 *
81 * See: https://dev.mysql.com/doc/refman/8.4/en/identifiers.html
82 */
83 if (
84 WP_MySQL_Lexer::BACK_TICK_QUOTED_ID === $this->id
85 || $this->sql_mode_no_backslash_escapes_enabled
86 ) {
87 return str_replace( $quote . $quote, $quote, $value );
88 }
89
90 /**
91 * Unescape MySQL escape sequences.
92 *
93 * MySQL string literals use backslash as an escape character, and
94 * the string bounding quotes can also be escaped by being doubled.
95 *
96 * The escaping is done according to the following rules:
97 *
98 * 1. Some special character escape sequences are recognized.
99 * For example, "\n" is a newline character, "\0" is ASCII NULL.
100 * 2. A specific treatment is applied to "\%" and "\_" sequences.
101 * This is due to their special meaning for pattern matching.
102 * 3. Other backslash-prefixed characters resolve to their literal
103 * values. For example, "\x" represents "x", "\\" represents "\".
104 *
105 * Despite looking similar, these rules are different from the C-style
106 * string escaping, so we cannot use "strip(c)slashes()" in this case.
107 *
108 * See: https://dev.mysql.com/doc/refman/8.4/en/string-literals.html
109 */
110 $backslash = chr( 92 );
111 $replacements = array(
112 /*
113 * MySQL special character escape sequences.
114 */
115 ( $backslash . '0' ) => chr( 0 ), // An ASCII NULL character (\0).
116 ( $backslash . "'" ) => chr( 39 ), // A single quote character (').
117 ( $backslash . '"' ) => chr( 34 ), // A double quote character (").
118 ( $backslash . 'b' ) => chr( 8 ), // A backspace character.
119 ( $backslash . 'n' ) => chr( 10 ), // A newline (linefeed) character (\n).
120 ( $backslash . 'r' ) => chr( 13 ), // A carriage return character (\r).
121 ( $backslash . 't' ) => chr( 9 ), // A tab character (\t).
122 ( $backslash . 'Z' ) => chr( 26 ), // An ASCII 26 (Control+Z) character.
123
124 /*
125 * Normalize escaping of "%" and "_" characters.
126 *
127 * MySQL has unusual handling for "\%" and "\_" in all string literals.
128 * While other sequences follow the C-style escaping ("\?" is "?", etc.),
129 * "\%" resolves to "\%" and "\_" resolves to "\_" (unlike in C strings).
130 *
131 * This means that "\%" behaves like "\\%", and "\_" behaves like "\\_".
132 * To preserve this behavior, we need to add a second backslash here.
133 *
134 * From https://dev.mysql.com/doc/refman/8.4/en/string-literals.html:
135 * > The \% and \_ sequences are used to search for literal instances
136 * > of % and _ in pattern-matching contexts where they would otherwise
137 * > be interpreted as wildcard characters. If you use \% or \_ outside
138 * > of pattern-matching contexts, they evaluate to the strings \% and
139 * > \_, not to % and _.
140 */
141 ( $backslash . '%' ) => $backslash . $backslash . '%',
142 ( $backslash . '_' ) => $backslash . $backslash . '_',
143
144 /*
145 * Preserve a double backslash as-is, so that the trailing backslash
146 * is not consumed as the beginning of an escape sequence like "\n".
147 *
148 * Resolving "\\" to "\" will be handled in the next step, where all
149 * other backslash-prefixed characters resolve to their literal values.
150 */
151 ( $backslash . $backslash )
152 => $backslash . $backslash,
153
154 /*
155 * The bounding quotes can also be escaped by being doubled.
156 */
157 ( $quote . $quote ) => $quote,
158 );
159
160 /*
161 * Apply the replacements.
162 *
163 * It is important to use "strtr()" and not "str_replace()", because
164 * "str_replace()" applies replacements one after another, modifying
165 * intermediate changes rather than just the original string:
166 *
167 * - str_replace( [ 'a', 'b' ], [ 'b', 'c' ], 'ab' ); // 'cc' (bad)
168 * - strtr( 'ab', [ 'a' => 'b', 'b' => 'c' ] ); // 'bc' (good)
169 */
170 $value = strtr( $value, $replacements );
171
172 /*
173 * A backslash with any other character represents the character itself.
174 * That is, \x evaluates to x, \\ evaluates to \, and \🙂 evaluates to 🙂.
175 *
176 * Use "s" (DOTALL) to match all characters including newlines. Do not use
177 * "u", so non-UTF-8 bytes are preserved (binary or single-byte charsets).
178 * Legacy multi-byte charsets like big5 or gbk, where a trail byte can be
179 * a backslash, are not supported.
180 */
181 $preg_quoted_backslash = preg_quote( $backslash );
182 $value = preg_replace( "/$preg_quoted_backslash(.)/s", '$1', $value );
183 }
184 return $value;
185 }
186
187 /**
188 * Get the token representation as a string.
189 *
190 * This method is intended to be used only for testing and debugging purposes,
191 * when tokens need to be presented in a human-readable form. It should not
192 * be used in production code, as it's not performance-optimized.
193 *
194 * @return string
195 */
196 public function __toString(): string {
197 return $this->get_value() . '<' . $this->id . ',' . $this->get_name() . '>';
198 }
199 }
200