如何实现PHP流包装器来修改另一个包装器的输出?

时间:2018-10-30 18:40:15

标签: php xml stream xmlreader php-stream-wrappers

我使用PHP zip://流包装器逐行解析大型XML文件。例如:

$stream_uri = 'zip://' . __DIR__ . '/archive.zip#foo.xml';
$reader     = new XMLReader();
$reader->open( $stream_uri, null );
$reader->read();

while ( true ) {
    echo( $reader->readInnerXml() . PHP_EOL );
    if ( ! $reader->next() ) {
        break;
    }
}

XML文件经常包含XMLReader不喜欢的狡猾的UTF控制字符。因此,我想实现一个自定义的流包装器,我可以将zip://流的输出传递给它,这将在每行上运行preg_replace来删除那些字符。

我的梦想是能够做到这一点:

stream_wrapper_register( 'xmlchars', 'XML_Chars' );
$stream_uri = 'xmlchars://zip://' . __DIR__ . '/archive.zip#foo.xml';

并让XMLReader愉快地阅读整理过的节点。我想出了一种基于传递到包装器的路径来重建zip流URI的方法:

class XML_Chars {

    protected $stream_uri = '';
    protected $handle;

    function stream_open( $path, $mode, $options, &$opened_path ) {
        $parsed_url     = parse_url( $path );
        $this->stream_uri = 'zip:' . $parsed_url['path'] . '#' . $parsed_url['fragment'];

        return true;
    }

}

但是我对打开zip://流的最佳方法感到困惑,因此我可以修改其输出并将结果传递到XMLReader。谁能给我有关如何实现该目标的任何指示?

1 个答案:

答案 0 :(得分:1)

如果对其他人有用,我发现了另一种解决问题的方法:流过滤器。您可以这样定义它:

class UTF_Character_Filter extends php_user_filter {
    public function filter( $in, $out, &$consumed, $closing ) {
        while ( $bucket = stream_bucket_make_writeable( $in ) ) {
            $consumed += $bucket->datalen;
            // Remove characters in the hex range 0 - 8, B and C, E to 1F
            // i.e. all control characters except newline, tab and return
            $bucket->data = preg_replace( '|[\x0-\x8\xB-\xC\xE-\x1F]|ms', '', $bucket->data );
            stream_bucket_append( $out, $bucket );
        }

        return PSFS_PASS_ON;
    }
}

stream_filter_register( 'utf_character_filter', 'UTF_Character_Filter' );

并像这样使用它:

php://filter/read=utf_character_filter/resource=zip://archive.zip#import.xml

我仍然想知道是否有人想出一种方法来制作一个可以接受另一个流包装器输入的流包装器,因为它可能是一个方便的工具。