vendor/doctrine-dbal/lib/Doctrine/DBAL/SQLParserUtils.php
changeset 0 7f95f8617b0b
equal deleted inserted replaced
-1:000000000000 0:7f95f8617b0b
       
     1 <?php
       
     2 /*
       
     3  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
       
     4  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
       
     5  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
       
     6  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
       
     7  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
       
     8  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
       
     9  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
       
    10  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
       
    11  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
       
    12  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
       
    13  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
       
    14  *
       
    15  * This software consists of voluntary contributions made by many individuals
       
    16  * and is licensed under the LGPL. For more information, see
       
    17  * <http://www.doctrine-project.org>.
       
    18  */
       
    19 
       
    20 
       
    21 namespace Doctrine\DBAL;
       
    22 
       
    23 use Doctrine\DBAL\Connection;
       
    24 
       
    25 /**
       
    26  * Utility class that parses sql statements with regard to types and parameters.
       
    27  *
       
    28  * @license     http://www.opensource.org/licenses/lgpl-license.php LGPL
       
    29  * @link        www.doctrine-project.com
       
    30  * @since       2.0
       
    31  * @author      Benjamin Eberlei <kontakt@beberlei.de>
       
    32  */
       
    33 class SQLParserUtils
       
    34 {
       
    35     /**
       
    36      * Get an array of the placeholders in an sql statements as keys and their positions in the query string.
       
    37      * 
       
    38      * Returns an integer => integer pair (indexed from zero) for a positional statement
       
    39      * and a string => int[] pair for a named statement.
       
    40      * 
       
    41      * @param string $statement
       
    42      * @param bool $isPositional
       
    43      * @return array
       
    44      */
       
    45     static public function getPlaceholderPositions($statement, $isPositional = true)
       
    46     {   
       
    47         $match = ($isPositional) ? '?' : ':';
       
    48         if (strpos($statement, $match) === false) {
       
    49             return array();
       
    50         }
       
    51         
       
    52         $count = 0;
       
    53         $inLiteral = false; // a valid query never starts with quotes
       
    54         $stmtLen = strlen($statement);
       
    55         $paramMap = array();
       
    56         for ($i = 0; $i < $stmtLen; $i++) {
       
    57             if ($statement[$i] == $match && !$inLiteral) {
       
    58                 // real positional parameter detected
       
    59                 if ($isPositional) {
       
    60                     $paramMap[$count] = $i;
       
    61                 } else {
       
    62                     $name = "";
       
    63                     // TODO: Something faster/better to match this than regex?
       
    64                     for ($j = $i; ($j < $stmtLen && preg_match('(([:a-zA-Z0-9]{1}))', $statement[$j])); $j++) {
       
    65                         $name .= $statement[$j];
       
    66                     }
       
    67                     $paramMap[$name][] = $i; // named parameters can be duplicated!
       
    68                     $i = $j;
       
    69                 }
       
    70                 ++$count;
       
    71             } else if ($statement[$i] == "'" || $statement[$i] == '"') {
       
    72                 $inLiteral = ! $inLiteral; // switch state!
       
    73             }
       
    74         }
       
    75 
       
    76         return $paramMap;
       
    77     }
       
    78     
       
    79     /**
       
    80      * For a positional query this method can rewrite the sql statement with regard to array parameters.
       
    81      * 
       
    82      * @param string $query
       
    83      * @param array $params
       
    84      * @param array $types 
       
    85      */
       
    86     static public function expandListParameters($query, $params, $types)
       
    87     {        
       
    88         $isPositional = is_int(key($params));
       
    89         $arrayPositions = array();
       
    90         $bindIndex = -1;
       
    91         foreach ($types AS $name => $type) {
       
    92             ++$bindIndex;
       
    93             if ($type === Connection::PARAM_INT_ARRAY || $type == Connection::PARAM_STR_ARRAY) {
       
    94                 if ($isPositional) {
       
    95                     $name = $bindIndex;
       
    96                 }
       
    97                 
       
    98                 $arrayPositions[$name] = false;
       
    99             }
       
   100         }
       
   101         
       
   102         if (!$arrayPositions || count($params) != count($types)) {
       
   103             return array($query, $params, $types);
       
   104         }
       
   105         
       
   106         $paramPos = self::getPlaceholderPositions($query, $isPositional);
       
   107         if ($isPositional) {
       
   108             $paramOffset = 0;
       
   109             $queryOffset = 0;
       
   110             foreach ($paramPos AS $needle => $needlePos) {
       
   111                 if (!isset($arrayPositions[$needle])) {
       
   112                     continue;
       
   113                 }
       
   114                 
       
   115                 $needle += $paramOffset;
       
   116                 $needlePos += $queryOffset;
       
   117                 $len = count($params[$needle]);
       
   118                 
       
   119                 $params = array_merge(
       
   120                     array_slice($params, 0, $needle),
       
   121                     $params[$needle],
       
   122                     array_slice($params, $needle + 1)
       
   123                 );
       
   124                 
       
   125                 $types = array_merge(
       
   126                     array_slice($types, 0, $needle),
       
   127                     array_fill(0, $len, $types[$needle] - Connection::ARRAY_PARAM_OFFSET), // array needles are at PDO::PARAM_* + 100
       
   128                     array_slice($types, $needle + 1)
       
   129                 );
       
   130                 
       
   131                 $expandStr = implode(", ", array_fill(0, $len, "?"));
       
   132                 $query = substr($query, 0, $needlePos) . $expandStr . substr($query, $needlePos + 1);
       
   133                 
       
   134                 $paramOffset += ($len - 1); // Grows larger by number of parameters minus the replaced needle.
       
   135                 $queryOffset += (strlen($expandStr) - 1);
       
   136             }
       
   137         } else {
       
   138             throw new DBALException("Array parameters are not supported for named placeholders.");
       
   139         }
       
   140         
       
   141         return array($query, $params, $types);
       
   142     }
       
   143 }