去评论
dz插件网

新人帖 使用原生DELET语句无空格时无法删除数据的问题 New

浅生
2026/01/16 13:52:31
1. 问题:



我使用DELETE语句删除数据的时候,如果引号内两侧不留空的时候,删除就不会执行,比如,下面这样就可以成功从数据库中删除:delpsqlf="DELETEFROMdatatable WHERE sid = sid";DB::query(delpsqlf);而下面这样,显示已执行,但数据库中的数据并未删除,数据库也无报错:delpsqlf="DELETEFROMdatatable WHERE sid = sid";DB::query(delpsqlf);


2. 千问的回答:



针对上述问题,我将discuz_database.php发给千问,给的结果为:


通过分析您提供的PHP类代码,我发现问题出在Discuz框架的SQL安全检查机制上。让我为您详细解释:


问题根源


问题主要出现在discuz_database_safecheck::checkquery()方法中,这个方法负责SQL语句的安全检查。关键代码如下:


$cmd = strtoupper(substr(trim($sql), 0, 3));if(isset(self::$checkcmd[$cmd])) {    $check = self::_do_query_safe($sql);}


这里self::$checkcmd定义为:


protected static $checkcmd = array('SEL'=>1, 'UPD'=>1, 'INS'=>1, 'REP'=>1, 'DEL'=>1);


具体分析


当SQL语句有前后空格时(" DELETE FROM table WHERE sid = sid"):trim(sql)会移除前后空格,得到"DELETE FROM table WHERE sid = sid"substr(trim(sql), 0, 3)返回"DEL""DEL"在checkcmd数组中存在,安全检查正常通过当SQL语句无前后空格时("DELETEFROMtableWHEREsid=sid"):同样经过trim()处理,理论上应该得到相同结果但在_do_query_safe()方法中,正则清理逻辑对格式高度敏感:


$clean = preg_replace("/[^a-z0-9_\-\(\)#\*\/\"]+/is", "", strtolower($clean));


这个复杂的清理逻辑可能因缺少空格而导致清理后的SQL字符串不包含"del"关键字最终安全检查失败,但框架没有抛出异常,而是静默失败


3. 智普的回答:



针对上述问题,我将discuz_database.php发给千问,给的结果为:


   这个问题出在 DB::query 方法中提取 SQL 命令类型的逻辑上。


问题分析


当你在 SQL 语句字符串($delpsqlf)的开头(引号内侧)添加空格时,DB::query 中的代码逻辑会导致返回值类型不一致,从而影响你的业务逻辑判断(例如 if(DB::query(...)))。请看 discuz_database 类中的 query 方法:


public static function query($sql, $arg = array(), $silent = false, $unbuffered = false) {    // ... 前面的代码省略 ...        // 执行 SQL 查询    $ret = self::$db->query($sql, $silent, $unbuffered);        if (!$unbuffered && $ret) {        // 【问题出在这里】        // 使用 strpos 查找第一个空格的位置来截取命令名称(如 DELETE, SELECT 等)        $cmd = trim(strtoupper(substr($sql, 0, strpos($sql, ' '))));                if ($cmd === 'SELECT') {            // ...        } elseif ($cmd === 'UPDATE' || $cmd === 'DELETE') {            // 如果命令是 UPDATE 或 DELETE,将返回值替换为受影响的行数            $ret = self::$db->affected_rows();        } elseif ($cmd === 'INSERT') {            $ret = self::$db->insert_id();        }    }    return $ret;}


为什么会有两种结果?


情况一:语句前面有空格(" DELETE FROM ...")strpos(sql,′′)会找到第一个空格,其位置是0。substr(sql, 0, 0) 返回空字符串 ""。cmd变成了空字符串。因为cmd 不等于 'DELETE',代码跳过了 ret=self::db->affected_rows(); 这一步。最终 DB::query 返回的是 MySQL 的结果对象(Resource/Object)。在 PHP 中,非空的对象转换为布尔值通常是 true。所以你的代码认为“执行成功”。


情况二:语句前面没有空格("DELETE FROM ...")strpos(sql,′′)会找到DELETE后面的空格,位置是6。substr(sql, 0, 6) 返回 "DELETE"。cmd变成了"DELETE"。代码进入了elseif(cmd === 'UPDATE' || cmd===′DELETE′)分支。ret 被赋值为 self::$db->affected_rows()(受影响的行数)。关键点: 如果 SQL 实际上没有删除任何数据(例如 sid 不匹配,或者你使用了事务未提交),affected_rows() 返回 0。你的代码判断 if(0) 结果为 false,所以你认为“执行失败”或“未删除”。


为什么数据库里实际上没被删除?


既然情况一显示“成功删除”,情况二显示“未删除”,但你提到情况二“数据库中的数据并未删除”,这意味着 affected_rows 确实返回了 0。这说明在两种情况下,发给数据库的 SQL 语句实际上可能都没有匹配到数据(或者 $sid 在第二种情况下的值有问题)。但是:


javascript



▼ 展开



我知道答案 回答被采纳将会获得1 贡献 已有3人回答