【问题标题】:PHP stream_get_contents() behaves strangely reading an LOB from a databasePHP stream_get_contents() 从数据库读取 LOB 的行为很奇怪
【发布时间】:2017-05-14 05:49:09
【问题描述】:

我的表中有 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);
}

结果如下所示:

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


4410-存储在 lob 中的长文本 ~ 5k 个字符

而不是我期望得到的结果是:

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


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

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

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

【问题讨论】:

  • 为什么...您为此使用stream_get_contents()?我是在想。你不是已经把结果拉进去了吗?为什么需要stream_get_contents()。这与使用 Oracle 有关吗?因为据我从手册中可以看出,stream_get_contents() 或多或少等同于file_get_contents(),除了它在流上运行......我看不到从数据库中检索到的资源如何以任何方式成为流?
  • 检索到的 LOB 是一个流:如果您简单地回显 $r['LOB_DEFINITION'],它将在屏幕上打印一个资源 ID。这个资源是一个句柄(类似于文件句柄),可以而且应该通过 stream_get_contents 或类似的东西来读取。它已经并且一直在为我的项目中的其他几个对象工作,这是我第一次循环它们,但似乎有一些有趣的事情发生。
  • 啊哈。不熟悉Oracle/LOB。因此问题。可能是关于输出内容后流没有拾取/释放的问题?也许有一个重置/停止/设置它?我看到这里有多个示例和建议:php.net/manual/en/pdo.lobs.php - 也许你可以找到一些有用的东西。

标签: php oracle pdo


【解决方案1】:

这是由于 PDO(OCI)::fetchAll 中的一个长期存在的错误 https://bugs.php.net/bug.php?id=46728

您不必像 Jeffs 的回答那样对每一行进行单独的查询。你只需要避免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;
}

【讨论】:

    【解决方案2】:

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

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

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

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

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

    在从表中检索到我想要使用的所有 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);
    }
    

    某种意义上的东西已经解决了我的问题(所有这些都以各种方法抽象出来,但本质上就是这样。

    【讨论】:

    • 谢谢!我按照您的建议解决了这个问题。
    • 我很高兴它有帮助
    猜你喜欢
    • 1970-01-01
    • 2023-01-17
    • 1970-01-01
    • 2017-02-11
    • 2012-09-24
    • 1970-01-01
    • 1970-01-01
    • 2014-01-07
    • 1970-01-01
    相关资源
    最近更新 更多