【问题标题】:Convert named parameters to unnamed in prepared statement in PHP在PHP中的准备语句中将命名参数转换为未命名参数
【发布时间】:2021-06-04 05:13:03
【问题描述】:

我有一个使用命名占位符的 SQL 查询,以及一个包含相应键和值的关联数组。

INSERT INTO table1 (a, b, c) VALUES (:a, :b, :c)
[
  'a' => 'ValueA',
  'c' => 'ValueC',
  'b' => 'ValueB'
]

现在我需要将查询和相应的参数转换为使用未命名(问号)。

INSERT INTO table1 (a, b, c) VALUES (?, ?, ?)
[
  0 => 'ValueA',
  1 => 'ValueB',
  2 => 'ValueC'
]

我怎样才能做到这一点,而不会冒着参数顺序错误的风险?

【问题讨论】:

  • 除了“确保以正确的顺序传递它们”之外,您希望得到什么样的答案?您是否正在考虑以编程方式编辑所有查询的大规模更改?
  • 我需要以与其他系统(不支持命名占位符)兼容的方式存储 SQL 查询和参数。但我仍然想在代码中使用命名占位符,以提高可读性并最大限度地减少意外以错误顺序提供参数的风险。不幸的是,并非所有系统都支持它们。

标签: php pdo prepared-statement


【解决方案1】:

我会使用preg_replace_callback 并将相应参数的值附加到新数组中:

/**
 * @return mixed[] An array containing two elements: the modified SQL query (string), 
 *                 and the modified params (array).
 */
function unnameSqlParameters(string $sql, array $params): array
{
    $newParams = [];

    $newSql = preg_replace_callback(
        '/:(\w+)/',
        static function (array $matches) use ($params, &$newParams): string {
            $name = $matches[1];

            if (!array_key_exists($name, $params)) {
                throw new \RuntimeException("Cannot find parameter value for :{$name}.");
            }

            $newParams[] = $params[$name];

            return '?';
        },
        $sql
    );

    return [$newSql, $newParams];
}

用法:

[$sql, $params] = unnameSqlParameters(
    'INSERT INTO table1 (a, b, c) VALUES (:a, :b, :c)',
    ['a' => 'ValueA', 'c' => 'ValueC', 'b' => 'ValueB']
);

Demo

请注意,使用的正则表达式也会匹配字符串中的参数(例如:a)。如果需要,有一些方法可以防止这种情况发生。

【讨论】:

  • 太棒了!我比我提出的解决方案更喜欢这个,因为它更容易理解,并且在一次操作中完成相同的任务。
【解决方案2】:

我想出了以下函数,其中考虑到了

  • 部分匹配占位符名称,即:b:bar
  • 参数在 SQL 与参数数组中以不同的顺序出现
function sqlNamedPlaceholdersToUnnamed(string &$sql, array &$params){
    // Sort params based on the order their keys appear in $sql
    $newParams = [];
    foreach($params as $key => $value){
        $result = preg_match("/:{$key}[^a-zA-Z0-9_]/", $sql, $matches, PREG_OFFSET_CAPTURE);

        if( $result !== 1 ) throw new \Exception("Unexpected result from preg_match. Expected 1, but got $result");
        $newParams[(int) $matches[0][1]] = $value;
    }
    ksort($newParams, SORT_NUMERIC);
    $newParams = array_values($newParams);


    // Replace named parameters with ? in $sql
    $newSql = preg_replace("/:[a-zA-Z0-9_]+/", '?', $sql, -1, $count);
    if( $newSql === null ) throw new \Exception('Error when executing preg_replace');
    if( $count !== count($params) ) throw new \Exception("Number of placeholders not same as number of params");


    // Replace arguments with results
    $sql = $newSql;
    $params = $newParams;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-11-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-16
    • 1970-01-01
    相关资源
    最近更新 更多