PHP stream_get_contents()从数据库读取LOB时的行为异常

PHP stream_get_contents()从数据库读取LOB时的行为异常,php,oracle,pdo,Php,Oracle,Pdo,我在一个表中有2条记录,具有相似的CLOB数据。其中每一个都是从查询中解析出来的,然后由php在循环中根据返回的PDO::FETCH_ASSOC读入字符串 在查询返回结果后,看起来是这样的: ID | NAME | DESCRIPTION | LOB_DEFINITION 4409 foo blah blah long text that is stored in a lob ~ 5k characters 4410 foob

我在一个表中有2条记录,具有相似的CLOB数据。其中每一个都是从查询中解析出来的,然后由php在循环中根据返回的PDO::FETCH_ASSOC读入字符串

在查询返回结果后,看起来是这样的:

ID   |   NAME   |   DESCRIPTION   |   LOB_DEFINITION
4409     foo        blah blah         long text that is stored in a lob ~ 5k characters
4410     foobar     blah blah         some other long text stored in a lob ~ 5k characters
我的问题是,一旦我开始循环并读取LOB中的内容,它们不会“重置”,因此,任何和所有读取的对象都将与它读取的第一个LOB具有相同的值:

大概是这样的:

foreach($result as $r) {
$lob = stream_get_contents($r['LOB_DEFINITION']);
echo $r['ID'].'-<i>'.$lob.'</i><HR>';
unset($lob);
}
foreach($r结果){
$lob=stream_get_contents($r['lob_DEFINITION']);
echo$r['ID'].-'.$lob.
; 未结算(lob); }
结果输出如下所示:

foreach($result as $r) {
$lob = stream_get_contents($r['LOB_DEFINITION']);
echo $r['ID'].'-<i>'.$lob.'</i><HR>';
unset($lob);
}
4409长文本,存储在lob~5k个字符中


4410以lob~5k字符存储的长文本

而不是我期望得到的:

4409长文本,存储在lob~5k个字符中


4410存储在lob~5k字符中的其他一些长文本

我也检查了资源ID,它们是不同的资源


我在这里不知所措。。。有人对这个问题有见解吗?

经过几个小时的研究,得出以下结论:

从oracle表(其中一列为clob数据类型)返回多记录集将导致最后检索到的记录clob成为所有检索到的clob的值。这就是导致上述行为的原因

我最终解决这个问题的方法是:

选择我计划使用CLOB的所有记录(不是CLOB本身,只是记录ID)

然后,我循环遍历这些记录ID,并使用单独的数据库调用,使用主键逐个处理CLOB数据,以拉入记录,确保返回的CLOB数据不超过1条。因此,本质上这就是现在正在发生的事情:

从表中检索到我要使用的所有CLOB的ID后,我对它们进行解析:

foreach($result as $r) {
  $query = 'select lob_definition from mytable where id = '.$r;
  $lobresult = oracleConnection->query($query)->fetchAll(FETCH_COLUM);
    $lob = stream_get_contents($lobresult);
    echo $r['ID'].'-<i>'.$lob.'</i><HR>';
    unset($lob);
}
foreach($r结果){
$query='从id='的mytable中选择lob_定义。$r;
$lobresult=oracleConnection->query($query)->fetchAll(FETCH\u column);
$lob=stream\u get\u contents($lobresult);
echo$r['ID'].-'.$lob.
; 未结算(lob); }

某种效果解决了我的问题(所有这些都用各种方法抽象出来,但本质上就是这样发生的。

这是由于PDO(OCI)中的一个长期缺陷造成的::fetchAll

您不必像Jeffs answer中那样对每一行进行单独的查询。您只需避免fetchAll。根据错误描述中建议的解决方法,我使用以下函数来获取所有LOB:

function fetchAllLOB(PDOStatement $stmt): array
{
    $result = [];
    while ($row = $stmt->fetch()) {
        foreach ($row as $name => $value) {
            if (is_resource($value)) {
                $row[$name] = stream_get_contents($value);
            }
        }
        $result[] = $row;
    }
    return $result;
}

为什么…你要使用
stream\u get\u contents()
?我只是想知道。你还没有把结果拉进来吗?为什么你需要
stream\u get\u contents()
。这与使用Oracle有关吗?因为我在手册中看到,
stream\u get\u contents()
或多或少等同于
file\u get\u contents()
,只是它在一个流上运行……我看不出从数据库检索到的资源是如何以任何方式成为一个流的?检索到的LOB是一个流:如果你简单地回显$r['LOB\u DEFINITION'],它将在屏幕上打印一个资源ID。这个资源是一个句柄(类似于文件句柄)这可以也应该通过stream\u get\u内容或类似的内容来阅读。它正在并且已经在我的项目中为其他几个对象工作,但是这是我第一次在它们之间循环工作,似乎有一些有趣的事情在进行。啊哈。不熟悉Oracle/LOB。因此,这个问题。可能是吗关于流在输出内容后没有接收/释放的内容?可能有重置/停止/设置?我看到这里有多个示例和建议:-也许你可以找到一些有用的东西。谢谢!我根据你的建议解决了这个问题。我很高兴它有帮助