我正在编写一个自定义流包装器,用作使用内置http://流包装器的HTTP客户端类的单元测试中的存根.
具体来说,我需要通过调用自定义流包装器创建的流上的stream_get_Meta_data来控制’wrapper_data’键中返回的值.不幸的是,关于自定义流包装器的文档很糟糕,API似乎不直观.
自定义包装器中的哪个方法控制Meta wrapper_data响应?
使用底部的类我只能在var_dump(stream_get_Meta_data($stream))时得到以下结果;在使用自定义包装器创建的流上…
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://流包装器返回的数据的解析…
array(10) { 'wrapper_data' => Array( [0] => HTTP/1.1 200 OK [1] => Content-Length: 438 ) ...
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_data在
ext/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流 – > wrapperdata保持被$retval [“wrapper_data”]“复制”/引用.
您的自定义包装器代码由user_wrapper_opener在main/streams/userspace.c“处理”.您有
/* set wrapper data to be a reference to our object */ stream->wrapperdata = us->object;
us-> object“is”已为流实例化的自定义包装器的实例.我还没有找到一种方法来影响来自其他用户空间脚本的stream-> wrapperdata.
但是你可以实现Iterator/IteratorAggregate和/或ArrayAccess,如果你需要的只是foreach($Metadata [‘wrapper_data’] …)和$Metadata [‘wrapper_data’] [$i].
例如.
<?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,&$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" } }