Chris@0
|
1 <?php
|
Chris@0
|
2 namespace GuzzleHttp\Psr7;
|
Chris@0
|
3
|
Chris@0
|
4 use Psr\Http\Message\StreamInterface;
|
Chris@0
|
5
|
Chris@0
|
6 /**
|
Chris@0
|
7 * Compose stream implementations based on a hash of functions.
|
Chris@0
|
8 *
|
Chris@0
|
9 * Allows for easy testing and extension of a provided stream without needing
|
Chris@0
|
10 * to create a concrete class for a simple extension point.
|
Chris@0
|
11 */
|
Chris@0
|
12 class FnStream implements StreamInterface
|
Chris@0
|
13 {
|
Chris@0
|
14 /** @var array */
|
Chris@0
|
15 private $methods;
|
Chris@0
|
16
|
Chris@0
|
17 /** @var array Methods that must be implemented in the given array */
|
Chris@0
|
18 private static $slots = ['__toString', 'close', 'detach', 'rewind',
|
Chris@0
|
19 'getSize', 'tell', 'eof', 'isSeekable', 'seek', 'isWritable', 'write',
|
Chris@0
|
20 'isReadable', 'read', 'getContents', 'getMetadata'];
|
Chris@0
|
21
|
Chris@0
|
22 /**
|
Chris@0
|
23 * @param array $methods Hash of method name to a callable.
|
Chris@0
|
24 */
|
Chris@0
|
25 public function __construct(array $methods)
|
Chris@0
|
26 {
|
Chris@0
|
27 $this->methods = $methods;
|
Chris@0
|
28
|
Chris@0
|
29 // Create the functions on the class
|
Chris@0
|
30 foreach ($methods as $name => $fn) {
|
Chris@0
|
31 $this->{'_fn_' . $name} = $fn;
|
Chris@0
|
32 }
|
Chris@0
|
33 }
|
Chris@0
|
34
|
Chris@0
|
35 /**
|
Chris@0
|
36 * Lazily determine which methods are not implemented.
|
Chris@0
|
37 * @throws \BadMethodCallException
|
Chris@0
|
38 */
|
Chris@0
|
39 public function __get($name)
|
Chris@0
|
40 {
|
Chris@0
|
41 throw new \BadMethodCallException(str_replace('_fn_', '', $name)
|
Chris@0
|
42 . '() is not implemented in the FnStream');
|
Chris@0
|
43 }
|
Chris@0
|
44
|
Chris@0
|
45 /**
|
Chris@0
|
46 * The close method is called on the underlying stream only if possible.
|
Chris@0
|
47 */
|
Chris@0
|
48 public function __destruct()
|
Chris@0
|
49 {
|
Chris@0
|
50 if (isset($this->_fn_close)) {
|
Chris@0
|
51 call_user_func($this->_fn_close);
|
Chris@0
|
52 }
|
Chris@0
|
53 }
|
Chris@0
|
54
|
Chris@0
|
55 /**
|
Chris@17
|
56 * An unserialize would allow the __destruct to run when the unserialized value goes out of scope.
|
Chris@17
|
57 * @throws \LogicException
|
Chris@17
|
58 */
|
Chris@17
|
59 public function __wakeup()
|
Chris@17
|
60 {
|
Chris@17
|
61 throw new \LogicException('FnStream should never be unserialized');
|
Chris@17
|
62 }
|
Chris@17
|
63
|
Chris@17
|
64 /**
|
Chris@0
|
65 * Adds custom functionality to an underlying stream by intercepting
|
Chris@0
|
66 * specific method calls.
|
Chris@0
|
67 *
|
Chris@0
|
68 * @param StreamInterface $stream Stream to decorate
|
Chris@0
|
69 * @param array $methods Hash of method name to a closure
|
Chris@0
|
70 *
|
Chris@0
|
71 * @return FnStream
|
Chris@0
|
72 */
|
Chris@0
|
73 public static function decorate(StreamInterface $stream, array $methods)
|
Chris@0
|
74 {
|
Chris@0
|
75 // If any of the required methods were not provided, then simply
|
Chris@0
|
76 // proxy to the decorated stream.
|
Chris@0
|
77 foreach (array_diff(self::$slots, array_keys($methods)) as $diff) {
|
Chris@0
|
78 $methods[$diff] = [$stream, $diff];
|
Chris@0
|
79 }
|
Chris@0
|
80
|
Chris@0
|
81 return new self($methods);
|
Chris@0
|
82 }
|
Chris@0
|
83
|
Chris@0
|
84 public function __toString()
|
Chris@0
|
85 {
|
Chris@0
|
86 return call_user_func($this->_fn___toString);
|
Chris@0
|
87 }
|
Chris@0
|
88
|
Chris@0
|
89 public function close()
|
Chris@0
|
90 {
|
Chris@0
|
91 return call_user_func($this->_fn_close);
|
Chris@0
|
92 }
|
Chris@0
|
93
|
Chris@0
|
94 public function detach()
|
Chris@0
|
95 {
|
Chris@0
|
96 return call_user_func($this->_fn_detach);
|
Chris@0
|
97 }
|
Chris@0
|
98
|
Chris@0
|
99 public function getSize()
|
Chris@0
|
100 {
|
Chris@0
|
101 return call_user_func($this->_fn_getSize);
|
Chris@0
|
102 }
|
Chris@0
|
103
|
Chris@0
|
104 public function tell()
|
Chris@0
|
105 {
|
Chris@0
|
106 return call_user_func($this->_fn_tell);
|
Chris@0
|
107 }
|
Chris@0
|
108
|
Chris@0
|
109 public function eof()
|
Chris@0
|
110 {
|
Chris@0
|
111 return call_user_func($this->_fn_eof);
|
Chris@0
|
112 }
|
Chris@0
|
113
|
Chris@0
|
114 public function isSeekable()
|
Chris@0
|
115 {
|
Chris@0
|
116 return call_user_func($this->_fn_isSeekable);
|
Chris@0
|
117 }
|
Chris@0
|
118
|
Chris@0
|
119 public function rewind()
|
Chris@0
|
120 {
|
Chris@0
|
121 call_user_func($this->_fn_rewind);
|
Chris@0
|
122 }
|
Chris@0
|
123
|
Chris@0
|
124 public function seek($offset, $whence = SEEK_SET)
|
Chris@0
|
125 {
|
Chris@0
|
126 call_user_func($this->_fn_seek, $offset, $whence);
|
Chris@0
|
127 }
|
Chris@0
|
128
|
Chris@0
|
129 public function isWritable()
|
Chris@0
|
130 {
|
Chris@0
|
131 return call_user_func($this->_fn_isWritable);
|
Chris@0
|
132 }
|
Chris@0
|
133
|
Chris@0
|
134 public function write($string)
|
Chris@0
|
135 {
|
Chris@0
|
136 return call_user_func($this->_fn_write, $string);
|
Chris@0
|
137 }
|
Chris@0
|
138
|
Chris@0
|
139 public function isReadable()
|
Chris@0
|
140 {
|
Chris@0
|
141 return call_user_func($this->_fn_isReadable);
|
Chris@0
|
142 }
|
Chris@0
|
143
|
Chris@0
|
144 public function read($length)
|
Chris@0
|
145 {
|
Chris@0
|
146 return call_user_func($this->_fn_read, $length);
|
Chris@0
|
147 }
|
Chris@0
|
148
|
Chris@0
|
149 public function getContents()
|
Chris@0
|
150 {
|
Chris@0
|
151 return call_user_func($this->_fn_getContents);
|
Chris@0
|
152 }
|
Chris@0
|
153
|
Chris@0
|
154 public function getMetadata($key = null)
|
Chris@0
|
155 {
|
Chris@0
|
156 return call_user_func($this->_fn_getMetadata, $key);
|
Chris@0
|
157 }
|
Chris@0
|
158 }
|