如何使用PHP在SQL Server中转义字符串?

94
我正在寻找SQL Server中mysql_real_escape_string()的替代方法。除了使用addslashes()之外,是否还有其他可用的替代函数?
另外,如果能提供mysql_error()的替代方法也会很有帮助。

3
对我来说,这不是一个重复的问题,因为它涉及到一个特定的 MSSQL 情况,而这种情况并没有相关的官方 PDO。 - Pierre de LESPINAY
函数 mysql_real_escape_string() 在 PHP 5.5.0 中被弃用,并在 PHP 7.0.0 中移除。 - Peter Mortensen
14个回答

75

addslashes()不是完全足够的,但PHP的mssql包没有提供任何好的替代方法。丑陋但完全通用的解决方案是将数据编码为十六进制字节串,即

$unpacked = unpack('H*hex', $data);
mssql_query('
    INSERT INTO sometable (somecolumn)
    VALUES (0x' . $unpacked['hex'] . ')
');

抽象一下,那就是:

function mssql_escape($data) {
    if(is_numeric($data))
        return $data;
    $unpacked = unpack('H*hex', $data);
    return '0x' . $unpacked['hex'];
}

mssql_query('
    INSERT INTO sometable (somecolumn)
    VALUES (' . mssql_escape($somevalue) . ')
');

mysql_error()的等价函数是mssql_get_last_message()


1
糟糕,应该是SELECT SCOPE_IDENTITY()! - Astra
4
嗯,很好,除了实际上它确实存在问题。我想你不会解释你认为问题是什么吧? - chaos
3
你尝试过在日期时间列上使用这个吗?我遇到了这个错误:SQLSTATE[22007]: Invalid datetime format: 210 [Microsoft][ODBC SQL Server Driver][SQL Server]Conversion failed when converting datetime from binary/varbinary string. 我认为只有当这种方法适用于每种 MSSQL 数据类型时,它才能正确。 - Alejandro García Iglesias
1
mssql_escape() 函数返回的内容对我来说不起作用。在执行选择操作后,文本显示为 0x4a2761696d65206269656e206c652063686f636f6c6174,因此无法阅读。 - Jeff Noel
3
你可能在用单引号或双引号将字符串包括起来。因为该项被转换为十六进制,所以引号不是必需的。SQL Server应该将十六进制值转换为数据库能够理解的内容。 - danielson317
显示剩余19条评论

42
function ms_escape_string($data) {
        if ( !isset($data) or empty($data) ) return '';
        if ( is_numeric($data) ) return $data;

        $non_displayables = array(
            '/%0[0-8bcef]/',            // url encoded 00-08, 11, 12, 14, 15
            '/%1[0-9a-f]/',             // url encoded 16-31
            '/[\x00-\x08]/',            // 00-08
            '/\x0b/',                   // 11
            '/\x0c/',                   // 12
            '/[\x0e-\x1f]/'             // 14-31
        );
        foreach ( $non_displayables as $regex )
            $data = preg_replace( $regex, '', $data );
        $data = str_replace("'", "''", $data );
        return $data;
    }

这里的部分代码是从CodeIgniter中借鉴来的。 它运行良好,是一个干净的解决方案。

编辑: 上述代码片段存在许多问题。请不要在未阅读评论以了解这些问题的情况下使用它。更好的选择是根本不要使用它。参数化查询是您的朋友:http://php.net/manual/en/pdo.prepared-statements.php


1
你为什么需要preg_replace?str_replace 不足吗? - Gabe
gabe:这里使用 preg_replace 函数是为了让我能够在正则表达式字符类中使用所提供的范围。否则这个操作会有更多的字符串替换操作。 - genio
8
引用函数并不负责篡改数据,它的职责仅是确保字符串格式正确,可以被添加到SQL语句中并且不会被修改。 - cHao
7
很抱歉,但是从第一行代码开始就有错误了 - empty($value) 不仅会返回 '',还会返回 null0'0'!因此,在所有这些情况下,您都会返回一个空字符串。请修改代码。 - Nux
1
我点赞了这个,我认为只要你完全意识到上述问题,它就是一个完全可以的函数。不过我会把它叫做ms_escape_and_strip_string,这样其他人在处理它时就能看出它执行了这两个任务。在多种情况下返回空字符串是可以接受的,只要你考虑到了它,除非我漏掉了更重要的一点。如果这不符合您的需求,您可以随时将该行删除并用适合您需求的逻辑替换它。 - NateDSaint
这并不包括所有的URL代码,例如空格 = %20。 - patricia

18

当你可以在查询中使用参数时,为什么还要费心去转义任何东西?!

sqlsrv_query(
    $connection, 
    'UPDATE some_table SET some_field = ? WHERE other_field = ?', 
    array($_REQUEST['some_field'], $_REQUEST['id'])
)

无论您的值参数是否为null,在选择、删除、更新中都可以正常工作。 坚持原则——不要连接SQL,这样您就始终安全,并且查询的可读性更好。

http://php.net/manual/en/function.sqlsrv-query.php


这是正确的方法。你应该总是使用参数而不是临时查询。然而,OP没有使用sqlsrv驱动程序。他正在使用mssql驱动程序。所以对于那些被困在使用sqlsrv驱动程序的人来说,可以使用以下链接:http://php.net/manual/en/function.mssql-query.php。 - smulholland2
2
@smulholland2 你是不是想说“或者那些被困在使用_MSSQL_驱动程序的人”? - Konstantin
一种情况可能是,如果您想生成一个包含INSERT语句的文件,在数据迁移方案中使用。 - Gary Reckard

11

您可以研究一下PDO库。您可以使用PDO的预处理语句,如果正确使用预处理语句,PDO会自动转义字符串中的任何错误字符。我认为这仅适用于PHP 5。


在我看来,PDO 的一些半吊子行为让我必须进行严格的测试,才能确保它正确地转义所有数据。 - chaos
@Chaos 真的吗?我不知道.. 你有一篇文章的链接吗? - alex
我所思考的是这个人昨天在PDO上遇到的问题。虽然与事务无关,但并不令人印象深刻。再加上PHP中不充分的数据转义历史(php.net告诉人们使用addslashes()!),我变得非常怀疑。 - chaos
2
我喜欢PDO并首先尝试了它,但是针对MSSQL的那个(在Unix上,基于dblib)有时会失败(分段错误),这就是为什么我转而使用上面定义的mssql_escape的原因。 - lapo
感谢您的评论,@Iapo。我正在考虑为一个mssql项目切换到PDO - 特别是为了获得转义 - 但是你已经帮我省去了麻烦。 - Winfield Trail

5

处理单引号和双引号的另一种方式是:

function mssql_escape($str)
{
    if(get_magic_quotes_gpc())
    {
        $str = stripslashes($str);
    }
    return str_replace("'", "''", $str);
}

get_magic_quotes_gpc函数已于PHP 5.3.0版本被弃用,并在PHP 5.4.0版本中移除。请参阅http://php.net/manual/en/info.configuration.php#ini.magic-quotes-gpc。 - Jan

4
为了避免单引号和双引号的影响,您需要将它们加倍:
$value = 'This is a quote, "I said, 'Hi'"';

$value = str_replace( "'", "''", $value ); 

$value = str_replace( '"', '""', $value );

$query = "INSERT INTO TableName ( TextFieldName ) VALUES ( '$value' ) ";

等等...
并且归属:Microsoft SQL Server 2000中的转义字符

2

在苦苦挣扎几个小时后,我想出了一个几乎最佳的解决方案。

将值转换为十六进制字符串的混沌答案并不能适用于每种数据类型,特别是日期时间列。

我使用 PHP 的 PDO::quote(),但由于它随 PHP 一起发布,PDO::quote() 不支持 MS SQL Server 并返回 FALSE。让它工作的解决方案是下载一些微软捆绑包:

之后,您可以使用像以下示例一样的 DSN 在 PHP 中连接 PDO:

sqlsrv:Server=192.168.0.25; Database=My_Database;

在DSN中使用UIDPWD参数无效,因此在创建连接时,在PDO构造函数的第二个和第三个参数中传递用户名和密码。现在您可以使用PHP的PDO::quote()。享受吧。

1
警告:此函数已在PHP 7.0.0中删除。

http://php.net/manual/en/function.mssql-query.php

如果您仍在使用这些mssql_*函数,请记住它们已从PHP v7.0.0中删除。因此,这意味着您最终必须重写您的模型代码,以使用PDO库、sqlsrv_*等。如果您正在寻找带有“引用/转义”方法的东西,我建议使用PDO。
此函数的替代方法包括:PDO::query()、sqlsrv_query()和odbc_exec()。

1

0

最好也转义SQL保留字。例如:

function ms_escape_string($data) {
    if (!isset($data) or empty($data))
        return '';

    if (is_numeric($data))
        return $data;

    $non_displayables = array(
        '/%0[0-8bcef]/',        // URL encoded 00-08, 11, 12, 14, 15
        '/%1[0-9a-f]/',         // url encoded 16-31
        '/[\x00-\x08]/',        // 00-08
        '/\x0b/',               // 11
        '/\x0c/',               // 12
        '/[\x0e-\x1f]/',        // 14-31
        '/\27/'
    );
    foreach ($non_displayables as $regex)
        $data = preg_replace( $regex, '', $data);
    $reemplazar = array('"', "'", '=');
    $data = str_replace($reemplazar, "*", $data);
    return $data;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接