2012-07-31 45 views
7

我在使用內置的http://流封裝器的HTTP客戶機類的單元測試中編寫自定義流封裝器作爲存根。使用自定義流封裝器作爲PHP的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; 
    } 
} 
+0

怎麼樣'streamWrapper :: stream_metadata'?可能會幫助,但[docs](http://www.php.net/manual/en/streamwrapper.stream-metadata.php)似乎是另有說明。 – Christian 2012-07-31 07:33:14

+0

我很榮幸你的好意! – hakre 2012-07-31 14:03:25

回答

14

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於流> wrapperdata保持被 「複製」 到/由$ RETVAL [ 「wrapper_data」]中引用。
您的自定義包裝代碼由中的user_wrapper_opener「處理」。有你有

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

us->object「是」已實例化流定製包裝的實例。我還沒有找到一種方法來影響stream->wrapperdata以外的用戶空間腳本。
但是,如果您只需要的是foreach($metadata['wrapper_data'] ...)$metadata['wrapper_data'][$i],則可以實施Iterator/IteratorAggregate和/或ArrayAccess
例如

<?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" 
    } 
} 
+0

一個優雅且令人尷尬的簡單解決方案,解決了一個讓我進入圈子的問題。謝謝。 – rdlowrey 2012-07-31 14:08:56

+1

我敢打賭,如果這已經記錄在網站上,我也不會進入圈子。 – b01 2014-10-21 21:56:22