用户
 找回密码
 入住 CI 中国社区
搜索
查看: 4400|回复: 15
收起左侧

[讨论/交流] 深夜寂寞看CI,发现BUG一枚,请验收。。。

[复制链接]
发表于 2012-8-11 02:03:56 | 显示全部楼层 |阅读模式
CI 版本:2.1.2
文件:system/database/DB_driver.php
类:CI_DB_driver
方法:compile_binds($sql, $binds)
大约578行左右。
方法的描述是:Compile Bindings
该方法应该是为了实现sql语句的占位符替换成目标数据的功能,如:

INSERT INTO `table` (`colA`, `colB`) VALUES (?,?);     替换成   
INSERT INTO `table` (`colA`, `colB`) VALUES (数值1,数值2);

在此方法里还做了数据转意。

PHP复制代码
/**
         * Compile Bindings
         *
         * @access      public
         * @param       string  the sql statement
         * @param       array   an array of bind data
         * @return      string
         */

        function compile_binds($sql, $binds)
        {
                if (strpos($sql, $this->bind_marker) === FALSE)
                {
                        return $sql;
                }
 
                if ( ! is_array($binds))
                {
                        $binds = array($binds);
                }
 
                // Get the sql segments around the bind markers
                $segments = explode($this->bind_marker, $sql);
 
                // The count of bind should be 1 less then the count of segments
                // If there are more bind arguments trim it down
                if (count($binds) >= count($segments)) {
                        $binds = array_slice($binds, 0, count($segments)-1);
                }
 
                // Construct the binded query
                $result = $segments[0];
                $i = 0;
                foreach ($binds as $bind)
                {
                        $result .= $this->escape($bind);
                        $result .= $segments[++$i];
                }
 
                return $result;
        }
复制代码

看着一部分:
PHP复制代码
if ( ! is_array($binds))
                {
                        $binds = array($binds);
                }
复制代码

看图:


 楼主| 发表于 2012-8-11 02:05:40 | 显示全部楼层
今天才下载CI看的,希望一起进步
发表于 2012-8-11 09:20:59 | 显示全部楼层
从注释来看,这个 $binds 应该传递数组或者一个字符串,而不能是 1,2,3,4 这样的字符串,也就是说这个方法不会去拆分逗号分割的字符串。
 楼主| 发表于 2012-8-11 10:40:07 | 显示全部楼层
Hex 发表于 2012-8-11 09:20
从注释来看,这个 $binds 应该传递数组或者一个字符串,而不能是 1,2,3,4 这样的字符串,也就是说这个方法 ...

假如就想您说的,不是类似的1,2,3,4字符串,那他为什么要将一个字符串整合成一个一维数组?还要进行foreach??假如sql语句里占位符只能写一个,那为什么不直接替换,而大费周章的去遍历?我没说他会去拆分1,2,3,4,而是相反的将其作为一个数组元素
发表于 2012-8-11 14:13:25 | 显示全部楼层
cyb5201314 发表于 2012-8-11 10:40
假如就想您说的,不是类似的1,2,3,4字符串,那他为什么要将一个字符串整合成一个一维数组?还要进行foreac ...

先判断是不是数组,不是数组变成数组,这样的写法比较常见吧,就是为了后续可以统一按数组来处理。
占位符可以写多个,所以 $binds 可以传一个数组过来。
 楼主| 发表于 2012-8-11 15:51:43 | 显示全部楼层
本帖最后由 cyb5201314 于 2012-8-11 15:54 编辑
Hex 发表于 2012-8-11 14:13
先判断是不是数组,不是数组变成数组,这样的写法比较常见吧,就是为了后续可以统一按数组来处理。
占位 ...

我知道统一成数组很方便,但是请您看看这下面的代码好吗?$binds = array($binds);这句会把$binds变成一个一维数组,而他下面又对$binds进行了foreach,欲将$binds内的元素按顺序替换到占位符的位置,但是按您讲的,如果只是简单的获取一个数组,而且是一维数组,您觉得这是对的??就算是目标数值是一样的,那也只需要替换一下全部的占位符为目标数值就行了,何必弄成数组,再看下面,他将sql语句用占位符分割了,再遍历$binds的一维数组,将每一个元素连接在分割的sql里,出现的结果就是sql断开了,除非他的功能只能支持占位符为1个数量的,此时$binds可以是一个字符串,刚去英文网站找了已经改过的版本
PHP复制代码
/**
         * Compile Bindings
         *
         * @param        string        the sql statement
         * @param        array        an array of bind data
         * @return        string
         */

        public function compile_binds($sql, $binds)
        {
                if (empty($binds) OR empty($this->bind_marker) OR strpos($sql, $this->bind_marker) === FALSE)
                {
                        return $sql;
                }
                elseif ( ! is_array($binds))
                {
                        $binds = array($binds);
                        $bind_count = 1;
                }
                else
                {
                        // Make sure we're using numeric keys
                        $binds = array_values($binds);
                        $bind_count = count($binds);
                }
 
                // We'll need the marker length later
                $ml = strlen($this->bind_marker);
 
                // Make sure not to replace a chunk inside a string that happens to match the bind marker
                if ($c = preg_match_all("/'[^']*'/i", $sql, $matches))
                {
                        $c = preg_match_all('/'.preg_quote($this->bind_marker).'/i',
                                str_replace($matches[0],
                                        str_replace($this->bind_marker, str_repeat(' ', $ml), $matches[0]),
                                        $sql, $c),
                                $matches, PREG_OFFSET_CAPTURE);
 
                        // Bind values' count must match the count of markers in the query
                        if ($bind_count !== $c)
                        {
                                return $sql;
                        }
                }
                elseif (($c = preg_match_all('/'.preg_quote($this->bind_marker).'/i', $sql, $matches, PREG_OFFSET_CAPTURE)) !== $bind_count)
                {
                        return $sql;
                }
 
                do
                {
                        $c--;
                        $sql = substr_replace($sql, $this->escape($binds[$c]), $matches[0][$c][1], $ml);
                }
                while ($c !== 0);
 
                return $sql;
        }
复制代码

发表于 2012-8-11 17:35:02 | 显示全部楼层
cyb5201314 发表于 2012-8-11 15:51
我知道统一成数组很方便,但是请您看看这下面的代码好吗?$binds = array($binds);这句会把$binds变成一个 ...

这里的关键就是要看 $binds 是什么。
如果 $binds 是 1,2,3,4 就有问题,如果 $binds 是 array('1','2','3','4') 就没问题。
要看是谁调用的 compile_binds() 方法。调用的时候是如何传参的。
发表于 2012-8-11 17:43:40 | 显示全部楼层
cyb5201314 发表于 2012-8-11 15:51
我知道统一成数组很方便,但是请您看看这下面的代码好吗?$binds = array($binds);这句会把$binds变成一个 ...

我刚才又仔细看了下传参,这个参数手册规定是要传递一个数组,类似 array('123', 'abc', 'def') 这样的。
我看了一下,你这个是最新代码库里的代码,不过,感觉 $binds 处理没变,只是改变了一下替换占位符的代码。
发表于 2012-8-13 11:05:45 | 显示全部楼层
看了半天,没懂BUG在哪
发表于 2012-8-13 17:45:24 | 显示全部楼层
我看懂了。

本版积分规则