1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12:
13:
14: 15: 16: 17: 18: 19:
20: class Horde_Stream_Wrapper_Combine
21: {
22: 23: 24: 25: 26:
27: public $context;
28:
29: 30: 31: 32: 33:
34: protected $_data = array();
35:
36: 37: 38: 39: 40:
41: protected $_length = 0;
42:
43: 44: 45: 46: 47:
48: protected $_position = 0;
49:
50: 51: 52: 53: 54:
55: protected $_datapos = 0;
56:
57: 58: 59: 60: 61:
62: protected $_ateof = false;
63:
64: 65: 66: 67: 68: 69: 70: 71: 72: 73:
74: public function stream_open($path, $mode, $options, &$opened_path)
75: {
76: $opts = stream_context_get_options($this->context);
77: if (empty($opts['horde-combine']['data']) ||
78: !($opts['horde-combine']['data'] instanceof Horde_Stream_Wrapper_CombineStream)) {
79: throw new Exception('A combined stream must be created using the Horde_Stream_Wrapper_CombineStream interface.');
80: }
81:
82: $data = $opts['horde-combine']['data']->getData();
83:
84: reset($data);
85: while (list(,$val) = each($data)) {
86: if (is_string($val)) {
87: $fp = fopen('php://temp', 'r+');
88: fwrite($fp, $val);
89: } else {
90: $fp = $val;
91: }
92:
93: fseek($fp, 0, SEEK_END);
94: $length = ftell($fp);
95: rewind($fp);
96:
97: $this->_data[] = array(
98: 'fp' => $fp,
99: 'l' => $length,
100: 'p' => 0
101: );
102:
103: $this->_length += $length;
104: }
105:
106: return true;
107: }
108:
109: 110: 111: 112: 113: 114: 115:
116: public function stream_read($count)
117: {
118: if ($this->stream_eof()) {
119: return false;
120: }
121:
122: $out = '';
123:
124: while ($count) {
125: $tmp = &$this->_data[$this->_datapos];
126: $curr_read = min($count, $tmp['l'] - $tmp['p']);
127: $out .= fread($tmp['fp'], $curr_read);
128: $count -= $curr_read;
129: $this->_position += $curr_read;
130:
131: if ($this->_position == $this->_length) {
132: if ($count) {
133: $this->_ateof = true;
134: break;
135: } else {
136: $tmp['p'] += $curr_read;
137: }
138: } elseif ($count) {
139: $tmp = &$this->_data[++$this->_datapos];
140: rewind($tmp['fp']);
141: $tmp['p'] = 0;
142: } else {
143: $tmp['p'] += $curr_read;
144: }
145: }
146:
147: return $out;
148: }
149:
150: 151: 152: 153: 154: 155: 156:
157: public function stream_write($data)
158: {
159: $tmp = &$this->_data[$this->_datapos];
160:
161: $oldlen = $tmp['l'];
162: $res = fwrite($tmp['fp'], $data);
163: if ($res === false) {
164: return false;
165: }
166:
167: $tmp['p'] = ftell($tmp['fp']);
168: if ($tmp['p'] > $oldlen) {
169: $tmp['l'] = $tmp['p'];
170: $this->_length += ($tmp['l'] - $oldlen);
171: }
172:
173: return $res;
174: }
175:
176: 177: 178: 179: 180:
181: public function stream_tell()
182: {
183: return $this->_position;
184: }
185:
186: 187: 188: 189: 190:
191: public function stream_eof()
192: {
193: return $this->_ateof;
194: }
195:
196: 197: 198: 199: 200:
201: public function stream_stat()
202: {
203: return array(
204: 'dev' => 0,
205: 'ino' => 0,
206: 'mode' => 0,
207: 'nlink' => 0,
208: 'uid' => 0,
209: 'gid' => 0,
210: 'rdev' => 0,
211: 'size' => $this->_length,
212: 'atime' => 0,
213: 'mtime' => 0,
214: 'ctime' => 0,
215: 'blksize' => 0,
216: 'blocks' => 0
217: );
218: }
219:
220: 221: 222: 223: 224: 225: 226: 227:
228: public function stream_seek($offset, $whence)
229: {
230: $oldpos = $this->_position;
231: $this->_ateof = false;
232:
233: switch ($whence) {
234: case SEEK_SET:
235: $offset = $offset;
236: break;
237:
238: case SEEK_CUR:
239: $offset = $this->_position + $offset;
240: break;
241:
242: case SEEK_END:
243: $offset = $this->_length + $offset;
244: break;
245:
246: default:
247: return false;
248: }
249:
250: $count = $this->_position = min($this->_length, $offset);
251:
252: foreach ($this->_data as $key => $val) {
253: if ($count < $val['l']) {
254: $this->_datapos = $key;
255: $val['p'] = $count;
256: fseek($val['fp'], $count, SEEK_SET);
257: break;
258: }
259: $count -= $val['l'];
260: }
261:
262: return ($oldpos != $this->_position);
263: }
264:
265: }
266: