使用自定义流包装器作为 PHP 的 http://流包装器的测试存根 [英] Using a custom stream wrapper as test stub for PHP's http:// stream wrapper

查看:44
本文介绍了使用自定义流包装器作为 PHP 的 http://流包装器的测试存根的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在编写一个自定义流包装器,用作使用内置 http:// 流包装器的 HTTP 客户端类的单元测试中的存根.

I'm writing a custom stream wrapper to use as a stub in unit tests for an HTTP client class that uses the built-in http:// stream wrapper.

具体来说,我需要通过在自定义流包装器创建的流上调用 stream_get_meta_data 来控制 'wrapper_data' 键中返回的值.不幸的是,关于自定义流包装器的文档很糟糕,而且 API 似乎不直观.

Specifically, I need control over the value returned in the 'wrapper_data' key by calls to stream_get_meta_data on streams created by the custom stream wrapper. Unfortunately, the documentation on custom stream wrappers is woeful and the API seems unintuitive.

自定义包装器中的什么方法控制元 wrapper_data 响应?

What method in a custom wrapper controls the the meta wrapper_data response?

使用底部的类,当我 var_dump(stream_get_meta_data($stream)); 使用自定义包装器创建的流时,我只能得到以下结果......

Using the class at the bottom I've only been able to get the following result when I var_dump(stream_get_meta_data($stream)); on streams created with the custom wrapper ...

array(10) {
  'wrapper_data' =>
  class CustomHttpStreamWrapper#5 (3) {
    public $context =>
    resource(13) of type (stream-context)
    public $position =>
    int(0)
    public $bodyData =>
    string(14) "test body data"
  }
  ...

但我需要诱使包装器在元数据检索中产生类似以下内容的内容,以便我可以测试客户端类对真实 http:// 流包装器返回的数据的解析...

But I need to coax the wrapper into yielding something like the following on meta data retrieval so I can test the client class's parsing of the data returned by the real http:// stream wrapper ...

array(10) {
  'wrapper_data' => Array(
       [0] => HTTP/1.1 200 OK
       [1] => Content-Length: 438
   )
   ...

这是我目前用于自定义包装器的代码:

Here's the code I have currently for the custom wrapper:

class CustomHttpStreamWrapper {

    public $context;
    public $position = 0;
    public $bodyData = 'test body data';

    public function stream_open($path, $mode, $options, &$opened_path) {
        return true;
    }

    public function stream_read($count) {
        $this->position += strlen($this->bodyData);
        if ($this->position > strlen($this->bodyData)) {
            return false;
        }
        return $this->bodyData;
    }

    public function stream_eof() {
        return $this->position >= strlen($this->bodyData);
    }

    public function stream_stat() {
        return array('wrapper_data' => array('test'));
    }

    public function stream_tell() {
        return $this->position;
    }
}

推荐答案

stream_get_meta_dataext/standard/streamfunc.c.相关部分是

if (stream->wrapperdata) {
    MAKE_STD_ZVAL(newval);
    MAKE_COPY_ZVAL(&stream->wrapperdata, newval);

    add_assoc_zval(return_value, "wrapper_data", newval);
}

即zval stream->wrapperdata 保存的任何内容都被 $retval["wrapper_data"] 复制"到/引用.
您的自定义包装器代码由 user_wrapper_openermain/streams/userspace.c.你有

i.e. whatever zval stream->wrapperdata holds is "copied" to/referenced by $retval["wrapper_data"].
Your custom wrapper code is "handled" by user_wrapper_opener in main/streams/userspace.c. And there you have

/* set wrapper data to be a reference to our object */
stream->wrapperdata = us->object;

us->object 是"已为流实例化的自定义包装器的实例.除此以外,我还没有找到从用户空间脚本影响 stream->wrapperdata 的方法.
但是你可以实现 Iterator/IteratorAggregate 和/或 ArrayAccess 如果你只需要 foreach($metadata['wrapper_data'] ...)$metadata['wrapper_data'][$i].
例如

us->object "is" the instance of your custom wrapper that has been instantiated for the stream. I haven't found a way to influence stream->wrapperdata from userspace scripts other than that.
But you could implement Iterator/IteratorAggregate and/or ArrayAccess if all you need is foreach($metadata['wrapper_data'] ...) and $metadata['wrapper_data'][$i].
E.g.

<?php
function test() {
    stream_wrapper_register("mock", "CustomHttpStreamWrapper") or die("Failed to register protocol");
    $fp = fopen("mock://myvar", "r+");
    $md = stream_get_meta_data($fp);

    echo "Iterator / IteratorAggregate\n";
    foreach($md['wrapper_data'] as $e) {
        echo $e, "\n";
    }

    echo "\nArrayAccess\n";
    echo $md['wrapper_data'][0], "\n";

    echo "\nvar_dump\n";
    echo var_dump($md['wrapper_data']);
}

class CustomHttpStreamWrapper implements IteratorAggregate, ArrayAccess  {
    public $context;
    public $position = 0;
    public $bodyData = 'test body data';

    protected $foo = array('HTTP/1.1 200 OK', 'Content-Length: 438', 'foo: bar', 'ham: eggs');
    /* IteratorAggregate */
    public function getIterator() {
        return new ArrayIterator($this->foo);
    }
    /* ArrayAccess */
    public function offsetExists($offset) { return array_key_exists($offset, $this->foo); }
    public function offsetGet($offset ) { return $this->foo[$offset]; }
    public function offsetSet($offset, $value) { $this->foo[$offset] = $value; }
    public function offsetUnset($offset) { unset($this->foo[$offset]); }

    /* StreamWrapper */
    public function stream_open($path, $mode, $options, &$opened_path) {
        return true;
    }

    public function stream_read($count) {
        $this->position += strlen($this->bodyData);
        if ($this->position > strlen($this->bodyData)) {
            return false;
        }
        return $this->bodyData;
    }

    public function stream_eof() {
        return $this->position >= strlen($this->bodyData);
    }

    public function stream_stat() {
        return array('wrapper_data' => array('test'));
    }

    public function stream_tell() {
        return $this->position;
    }
}

test();

印刷品

Iterator / IteratorAggregate
HTTP/1.1 200 OK
Content-Length: 438
foo: bar
ham: eggs

ArrayAccess
HTTP/1.1 200 OK

var_dump
object(CustomHttpStreamWrapper)#1 (4) {
  ["context"]=>
  resource(5) of type (stream-context)
  ["position"]=>
  int(0)
  ["bodyData"]=>
  string(14) "test body data"
  ["foo":protected]=>
  array(4) {
    [0]=>
    string(15) "HTTP/1.1 200 OK"
    [1]=>
    string(19) "Content-Length: 438"
    [2]=>
    string(8) "foo: bar"
    [3]=>
    string(9) "ham: eggs"
  }
}

这篇关于使用自定义流包装器作为 PHP 的 http://流包装器的测试存根的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆