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