PHP函数 mysql_real_escape_string 与 addslashes 的区别

addslashes 和 mysql_real_escape_string 都是为了使数据安全的插入到数据库中而进行的过滤,那么这两个函数到底是有什么区别呢?

首先,我们还是从PHP手册入手:

手册上addslashes转义的字符是单引号(')、双引号(")、反斜线(\)与NUL(NULL 字符)。

mysql_real_escape_string转义的字符并没有被提到,只是说了一句:

注意:mysql_real_escape_string() 并不转义% 和_。

为什么PHP手册没有说呢?因为其实这是个MySql的C的API,所以我们需要查下MySql手册,上面是这么说的:

编码的字符为NUL (ASCII 0)、'\n'、'\r'、'\'、'''、'"'、以及Control-Z(请参见9.1节,“文字值”)。(严格地讲,MySQL仅需要反斜杠和引号字符,用于引用转义查询中的字符串。该函数能引用其他字符,从而使得它们在日志文件中具有更好的可读性)。

MySql手册上面的话总是令人费解的。 

我们为了更深层次的探究这两个函数的不同,还是去看一看PHP的源码吧。

这是PHP的addslashes函数:

 PHP_FUNCTION(addslashes)
    {
         zval **str;
        if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &str) == FAILURE) {
             WRONG_PARAM_COUNT;
         }
         convert_to_string_ex(str);
        if (Z_STRLEN_PP(str) == 0) {
             RETURN_EMPTY_STRING();
         }
         RETURN_STRING(php_addslashes(Z_STRVAL_PP(str),
                                      Z_STRLEN_PP(str),
                                      &Z_STRLEN_P(return_value), 0
                                      TSRMLS_CC), 0);
    }

很显然,它调用了 php_addslashes,我们继续看这个函数,

PHPAPI char *php_addslashes(char *str, int length, int *new_length, int should_free TSRMLS_DC)
    {
        return php_addslashes_ex(str, length, new_length, should_free, 0 TSRMLS_CC);
    }

结果又是在调用 php_addslashes_ex,我们就像在剥洋葱一样,一步一步的接近真理。

PHPAPI char *php_addslashes_ex(char *str, int length, int *new_length, int should_free, int ignore_sybase TSRMLS_DC)
    {
        /* maximum string length, worst case situation */
        char *new_str;
        char *source, *target;
        char *end;
        int local_new_length;
               
        if (!new_length) {
             new_length = &local_new_length;
         }
        if (!str) {
             *new_length = 0;
            return str;
         }
         new_str = (char *) safe_emalloc(2, (length ? length : (length = strlen(str))), 1);
         source = str;
         end = source + length;
         target = new_str;
       
        if (!ignore_sybase && PG(magic_quotes_sybase)) {
            while (source < end) {
                switch (*source) {
                    case '\0':
                         *target++ = '\\';
                         *target++ = '0';
                        break;
                    case '\'':
                         *target++ = '\'';
                         *target++ = '\'';
                        break;
                    default:
                         *target++ = *source;
                        break;
                 }
                 source++;
             }
         } else {
            while (source < end) {
                switch (*source) {
                    case '\0':
                         *target++ = '\\';
                         *target++ = '0';
                        break;
                    case '\'':
                    case '\"':
                    case '\\':
                         *target++ = '\\';
                        /* break is missing *intentionally* */
                    default:
                         *target++ = *source;
                        break; 
                 }
           
                 source++;
             }
         }
       
         *target = 0;
         *new_length = target - new_str;
        if (should_free) {
             STR_FREE(str);
         }
         new_str = (char *) erealloc(new_str, *new_length + 1);
        return new_str;
    }

上面的函数已经非常清楚的描述出都要转义哪些字符了,现在我们去看一看 mysql_real_escape_string

这个不在string.c里了,是在mysql扩展中。

PHP_FUNCTION(mysql_real_escape_string)
    {
         zval *mysql_link = NULL;
        char *str;
        char *new_str;
        int id = -1, str_len, new_str_len;
         php_mysql_conn *mysql;
        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|r", &str, &str_len, &mysql_link) == FAILURE) {
            return;
         }
        if (ZEND_NUM_ARGS() == 1) {
             id = php_mysql_get_default_link(INTERNAL_FUNCTION_PARAM_PASSTHRU);
             CHECK_LINK(id);
         }
         ZEND_FETCH_RESOURCE2(mysql, php_mysql_conn *, &mysql_link, id, "MySQL-Link", le_link, le_plink);
         new_str = safe_emalloc(str_len, 2, 1);
         new_str_len = mysql_real_escape_string(&mysql->conn, new_str, str, str_len);
         new_str = erealloc(new_str, new_str_len + 1);
         RETURN_STRINGL(new_str, new_str_len, 0);
    }

这个函数并没有像上面的那样剥洋葱,而是直接调用了MySql的C的API,mysql_real_escape_string()。

需要注意的是,这个函数在调用 mysql_real_escape_string 这个API之前,先是判断了是否连接上了数据库,

CHECK_LINK(id);   // 就是这句

所以,这就意味着 mysql_real_escape_string 必须是连接数据库之后才能使用。为了证实这一点,我们来简单的实验下:

<?php
echo mysql_real_escape_string("fdsafda'fdsa");

结果:

Warning: mysql_real_escape_string() [function.mysql-real-escape-string]: Access denied for user 'ODBC'@'localhost' (using password: NO) in PHPDocument1 on line 2
 
Warning: mysql_real_escape_string() [function.mysql-real-escape-string]: A link to the server could not be established in PHPDocument1 on line 2

果然报错了,显示没有链接上数据库。

好了,总结就先告一段落。

终于明白为什么那么多开源的程序比如 Discuz addslashes 而不用 mysql_real_escape_string 了。

所以呢,以后也就用 addslashes 好了,暂时可以忘记掉 mysql_real_escape_string 了!

posted @ 2016-07-17 09:18  52php  阅读(730)  评论(0编辑  收藏  举报