[ Index ]

PHP Cross Reference of YOURLS

title

Body

[close]

/includes/vendor/aura/sql/src/Parser/ -> AbstractParser.php (source)

   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  }


Generated: Thu Sep 19 05:10:04 2024 Cross-referenced by PHPXref 0.7.1