[ Index ] |
PHP Cross Reference of YOURLS |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * 4 * This file is part of Aura for PHP. 5 * 6 * @license https://opensource.org/licenses/MIT MIT 7 * 8 */ 9 namespace Aura\Sql\Parser; 10 11 use Aura\Sql\Exception\MissingParameter; 12 13 /** 14 * 15 * Parsing/rebuilding functionality for all drivers. 16 * 17 * Note that this does not validate the syntax; it only replaces/rebuilds 18 * placeholders in the query. 19 * 20 * @package Aura.Sql 21 * 22 */ 23 abstract class AbstractParser implements ParserInterface 24 { 25 /** 26 * 27 * Split the query string on these regexes. 28 * 29 * @var array 30 * 31 */ 32 protected $split = [ 33 // single-quoted string 34 "'(?:[^'\\\\]|\\\\'?)*'", 35 // double-quoted string 36 '"(?:[^"\\\\]|\\\\"?)*"', 37 ]; 38 39 /** 40 * 41 * Skip query parts matching this regex. 42 * 43 * @var string 44 * 45 */ 46 protected $skip = '/^(\'|\"|\:[^a-zA-Z_])/um'; 47 48 /** 49 * 50 * The current numbered-placeholder in the original statement. 51 * 52 * @var int 53 * 54 */ 55 protected $num = 0; 56 57 /** 58 * 59 * How many times has a named placeholder been used? 60 * 61 * @var array 62 * 63 */ 64 protected $count = [ 65 '__' => null, 66 ]; 67 68 /** 69 * 70 * The initial values to be bound. 71 * 72 * @var array 73 * 74 */ 75 protected $values = []; 76 77 /** 78 * 79 * Final placeholders and values to bind. 80 * 81 * @var array 82 * 83 */ 84 protected $final_values = []; 85 86 /** 87 * 88 * Rebuilds a statement with placeholders and bound values. 89 * 90 * @param string $statement The statement to rebuild. 91 * 92 * @param array $values The values to bind and/or replace into a statement. 93 * 94 * @return array An array where element 0 is the rebuilt statement and 95 * element 1 is the rebuilt array of values. 96 * 97 */ 98 public function rebuild($statement, array $values = []) 99 { 100 // match standard PDO execute() behavior of zero-indexed arrays 101 if (array_key_exists(0, $values)) { 102 array_unshift($values, null); 103 } 104 105 $this->values = $values; 106 $statement = $this->rebuildStatement($statement); 107 return [$statement, $this->final_values]; 108 } 109 110 /** 111 * 112 * Given a statement, rebuilds it with array values embedded. 113 * 114 * @param string $statement The SQL statement. 115 * 116 * @return string The rebuilt statement. 117 * 118 */ 119 protected function rebuildStatement($statement) 120 { 121 $parts = $this->getParts($statement); 122 return $this->rebuildParts($parts); 123 } 124 125 /** 126 * 127 * Given an array of statement parts, rebuilds each part. 128 * 129 * @param array $parts The statement parts. 130 * 131 * @return string The rebuilt statement. 132 * 133 */ 134 protected function rebuildParts(array $parts) 135 { 136 $statement = ''; 137 foreach ($parts as $part) { 138 $statement .= $this->rebuildPart($part); 139 } 140 return $statement; 141 } 142 143 /** 144 * 145 * Rebuilds a single statement part. 146 * 147 * @param string $part The statement part. 148 * 149 * @return string The rebuilt statement. 150 * 151 */ 152 protected function rebuildPart($part) 153 { 154 if (preg_match($this->skip, $part)) { 155 return $part; 156 } 157 158 // split into subparts by ":name" and "?" 159 $subs = preg_split( 160 "/(?<!:)(:[a-zA-Z_][a-zA-Z0-9_]*)|(\?)/um", 161 $part, 162 -1, 163 PREG_SPLIT_DELIM_CAPTURE 164 ); 165 166 // check subparts to expand placeholders for bound arrays 167 return $this->prepareValuePlaceholders($subs); 168 } 169 170 /** 171 * 172 * Prepares the sub-parts of a query with placeholders. 173 * 174 * @param array $subs The query subparts. 175 * 176 * @return string The prepared subparts. 177 * 178 */ 179 protected function prepareValuePlaceholders(array $subs) 180 { 181 $str = ''; 182 foreach ($subs as $i => $sub) { 183 $char = substr($sub, 0, 1); 184 if ($char == '?') { 185 $str .= $this->prepareNumberedPlaceholder(); 186 } elseif ($char == ':') { 187 $str .= $this->prepareNamedPlaceholder($sub); 188 } else { 189 $str .= $sub; 190 } 191 } 192 return $str; 193 } 194 195 /** 196 * 197 * Bind or quote a numbered placeholder in a query subpart. 198 * 199 * @return string The prepared query subpart. 200 * 201 * @throws MissingParameter 202 */ 203 protected function prepareNumberedPlaceholder() 204 { 205 $this->num ++; 206 if (array_key_exists($this->num, $this->values) === false) { 207 throw new MissingParameter("Parameter {$this->num} is missing from the bound values"); 208 } 209 210 $expanded = []; 211 $values = (array) $this->values[$this->num]; 212 if (is_null($this->values[$this->num])) { 213 $values[] = null; 214 } 215 foreach ($values as $value) { 216 $count = ++ $this->count['__']; 217 $name = "__{$count}"; 218 $expanded[] = ":{$name}"; 219 $this->final_values[$name] = $value; 220 } 221 return implode(', ', $expanded); 222 } 223 224 /** 225 * 226 * Bind or quote a named placeholder in a query subpart. 227 * 228 * @param string $sub The query subpart. 229 * 230 * @return string The prepared query subpart. 231 * 232 */ 233 protected function prepareNamedPlaceholder($sub) 234 { 235 $orig = substr($sub, 1); 236 if (array_key_exists($orig, $this->values) === false) { 237 throw new MissingParameter("Parameter '{$orig}' is missing from the bound values"); 238 } 239 240 $name = $this->getPlaceholderName($orig); 241 242 // is the corresponding data element an array? 243 $bind_array = is_array($this->values[$orig]); 244 if ($bind_array) { 245 // expand to multiple placeholders 246 return $this->expandNamedPlaceholder($name, $this->values[$orig]); 247 } 248 249 // not an array, retain the placeholder for later 250 $this->final_values[$name] = $this->values[$orig]; 251 return ":$name"; 252 } 253 254 /** 255 * 256 * Given an original placeholder name, return a replacement name. 257 * 258 * @param string $orig The original placeholder name. 259 * 260 * @return string 261 * 262 */ 263 protected function getPlaceholderName($orig) 264 { 265 if (! isset($this->count[$orig])) { 266 $this->count[$orig] = 0; 267 return $orig; 268 } 269 270 $count = ++ $this->count[$orig]; 271 return "{$orig}__{$count}"; 272 } 273 274 /** 275 * 276 * Given a named placeholder for an array, expand it for the array values, 277 * and bind those values to the expanded names. 278 * 279 * @param string $prefix The named placeholder. 280 * 281 * @param array $values The array values to be bound. 282 * 283 * @return string 284 * 285 */ 286 protected function expandNamedPlaceholder($prefix, array $values) 287 { 288 $i = 0; 289 $expanded = []; 290 foreach ($values as $value) { 291 $name = "{$prefix}_{$i}"; 292 $expanded[] = ":{$name}"; 293 $this->final_values[$name] = $value; 294 $i ++; 295 } 296 return implode(', ', $expanded); 297 } 298 299 /** 300 * 301 * Given a query string, split it into parts. 302 * 303 * @param string $statement The query string. 304 * 305 * @return array 306 * 307 */ 308 protected function getParts($statement) 309 { 310 $split = implode('|', $this->split); 311 return preg_split( 312 "/($split)/um", 313 $statement, 314 -1, 315 PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY 316 ); 317 } 318 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Tue Jan 21 05:10:11 2025 | Cross-referenced by PHPXref 0.7.1 |