php中user_filter的使用

php中user_filter的使用

golang中io库提供了统一的流操作方法,php中存在类似功能吗?答案是有的,并且同golang相似地提供了统一的操作流(网络、

文件、压缩数据等数据的抽象)的方法。也就是说php的流函数提供了处理不同流资源的统一接口。

流数据各式各样,针对不同的流自然需要不同的协议,这就是流封装协议

php中的流协议由schema://resource 组成,schema就是流封装协议,resource就是数据资源。

file_get_contentx('http://123.abc')
fopen('file://abc.txt', 'a')
fgets('php://stdin')

相比于完全实现自己的流封装协议(因为都替你实现好了,这里有个例子),工作中更可能用到的是在读取或写入的流的过程中对流进行写操作。这就可能使用到流上下文,作用是影响流的行为,同时不同的流封装协议有不同的上下文选项。如使用http流协议发送post请求

$postdata = http_build_query(
    array(
        'var1' => 'some content',
        'var2' => 'doh'
    )
);
$opts = array('http' =>
    array(
        'method'  => 'POST',
        'header'  => 'Content-type: application/x-www-form-urlencoded',
        'content' => $postdata
    )
);
$context = stream_context_create($opts);
$result = file_get_contents('http://example.com/submit.php', false, $context);

同样的我们可以自定义的流过滤器影响读写流的行为,可实现修改、过滤流等功能。下面是用user_filter实现一个简单的拆包过程

# 客户端 php已经提供了类似功能,这里只是演示,没什么实际意义的例子
# the filter really doesn't consume the connenction's buffer in this example, so there may be repetitive content, we can totally solve the situation in next episode
class unpack_filter extends php_user_filter
{
    public string $data = '';

    public function filter($in, $out, &$consumed, $closing)
    {
        while ($bucket = stream_bucket_make_writeable($in)) {
            $this->data .= $bucket->data;
            $consumed   += $bucket->datalen;
            $p          = explode("\n", $this->data);
            // TODO 完善分包处理
            $last = array_pop($p);
            if ($last !== '') {
                $this->data = $last;
            }
            array_walk($p, function ($pp) use ($in, $out, &$len) {
                if (!empty($pp)) {
                    $nb  = stream_bucket_new($this->stream, 'got filtered' . $pp . "test");
                    stream_bucket_append($out, $nb);
                }
            });
        }
        return PSFS_PASS_ON;
    }
}

$sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_connect($sock, '127.0.0.1', 9503);

$res = socket_export_stream($sock);
stream_filter_register('unpack_filter', 'unpack_filter');
stream_filter_append($res, 'unpack_filter');

while (1) {
    var_dump(stream_get_line($res, 0, "test"));
}
# 服务端 复制自swoole官网
$server = new Swoole\Server('127.0.0.1', 9503);

$server->on('start', function ($server) {
    echo "TCP Server is started at tcp://127.0.0.1:9503\n";
});

$server->on('connect', function ($server, $fd) {
    // $data = 'abcd1234';
    // $data = pack('N', strlen($data)) . $data;
    $data = "abcd1234\n";
    for ($i = 0; $i < 10000; $i++) {
        $server->send($fd, $data);
    }
});

$server->on('receive', function ($server, $fd, $reactor_id, $data) {
    $server->send($fd, "Swoole: {$data}");
});

$server->on('close', function ($server, $fd) {
    echo "connection close: {$fd}\n";
});

$server->start();

参考内置filter

posted @ 2022-03-05 23:06  alwayslinger  阅读(97)  评论(0编辑  收藏  举报