2
2
3
3
namespace Clue \React \Tar ;
4
4
5
- use React \Stream \WritableStream ;
6
- use React \Stream \ReadableStream ;
5
+ use Evenement \EventEmitter ;
6
+ use React \Stream \ThroughStream ;
7
+ use React \Stream \WritableStreamInterface ;
7
8
use RuntimeException ;
8
- use Exception ;
9
9
10
10
/**
11
11
* Decodes a TAR stream and emits "entry" events for each individual file in the archive.
14
14
* introduced by POSIX IEEE P1003.1. In the future, it should support more of
15
15
* the less common alternative formats.
16
16
*
17
- * @event entry(array $header, ReadableStream $stream, Decoder $thisDecoder )
18
- * @event error(Exception $e, Decoder $thisDecoder )
17
+ * @event entry(array $header, \React\Stream\ReadableStreamInterface $stream)
18
+ * @event error(Exception $e)
19
19
* @event close()
20
20
*/
21
- class Decoder extends WritableStream
21
+ class Decoder extends EventEmitter implements WritableStreamInterface
22
22
{
23
23
private $ buffer = '' ;
24
24
private $ writable = true ;
@@ -36,14 +36,14 @@ public function __construct()
36
36
37
37
if (PHP_VERSION < 5.5 ) {
38
38
// PHP 5.5 replaced 'a' with 'Z' (read X bytes and removing trailing NULL bytes)
39
- $ this ->format = str_replace ('Z ' , 'a ' , $ this ->format );
39
+ $ this ->format = str_replace ('Z ' , 'a ' , $ this ->format ); // @codeCoverageIgnore
40
40
}
41
41
}
42
42
43
43
public function write ($ data )
44
44
{
45
45
if (!$ this ->writable ) {
46
- return ;
46
+ return false ;
47
47
}
48
48
49
49
// incomplete entry => read until end of entry before expecting next header
@@ -52,7 +52,7 @@ public function write($data)
52
52
53
53
// entry still incomplete => wait for next chunk
54
54
if ($ this ->streaming !== null ) {
55
- return ;
55
+ return true ;
56
56
}
57
57
}
58
58
@@ -62,7 +62,7 @@ public function write($data)
62
62
63
63
// padding still remaining => wait for next chunk
64
64
if ($ this ->padding !== 0 ) {
65
- return ;
65
+ return true ;
66
66
}
67
67
}
68
68
@@ -79,32 +79,32 @@ public function write($data)
79
79
}
80
80
try {
81
81
$ header = $ this ->readHeader ($ header );
82
- } catch (Exception $ e ) {
82
+ } catch (RuntimeException $ e ) {
83
83
// clean up before throwing
84
84
$ this ->buffer = '' ;
85
85
$ this ->writable = false ;
86
86
87
- $ this ->emit ('error ' , array ($ e, $ this ));
87
+ $ this ->emit ('error ' , array ($ e ));
88
88
$ this ->close ();
89
- return ;
89
+ return false ;
90
90
}
91
91
92
- $ this ->streaming = new ReadableStream ();
92
+ $ this ->streaming = new ThroughStream ();
93
93
$ this ->remaining = $ header ['size ' ];
94
94
$ this ->padding = $ header ['padding ' ];
95
95
96
- $ this ->emit ('entry ' , array ($ header , $ this ->streaming , $ this ));
96
+ $ this ->emit ('entry ' , array ($ header , $ this ->streaming ));
97
97
98
98
if ($ this ->remaining === 0 ) {
99
- $ this ->streaming ->close ();
99
+ $ this ->streaming ->end ();
100
100
$ this ->streaming = null ;
101
101
} else {
102
102
$ this ->buffer = $ this ->consumeEntry ($ this ->buffer );
103
103
}
104
104
105
105
// incomplete entry => do not read next header
106
106
if ($ this ->streaming !== null ) {
107
- return ;
107
+ return true ;
108
108
}
109
109
110
110
if ($ this ->padding !== 0 ) {
@@ -113,9 +113,11 @@ public function write($data)
113
113
114
114
// incomplete padding => do not read next header
115
115
if ($ this ->padding !== 0 ) {
116
- return ;
116
+ return true ;
117
117
}
118
118
}
119
+
120
+ return true ;
119
121
}
120
122
121
123
public function end ($ data = null )
@@ -124,6 +126,22 @@ public function end($data = null)
124
126
$ this ->write ($ data );
125
127
}
126
128
129
+ if ($ this ->streaming !== null ) {
130
+ // input stream ended but we were still streaming an entry => emit error about incomplete entry
131
+ $ this ->streaming ->emit ('error ' , array (new \RuntimeException ('TAR input stream ended unexpectedly ' )));
132
+ $ this ->streaming ->close ();
133
+ $ this ->streaming = null ;
134
+
135
+ // add some dummy data to also trigger error on decoder stream
136
+ $ this ->buffer = '. ' ;
137
+ }
138
+
139
+ if ($ this ->buffer !== '' ) {
140
+ // incomplete entry in buffer
141
+ $ this ->emit ('error ' , array (new \RuntimeException ('Stream ended with incomplete entry ' )));
142
+ $ this ->buffer = '' ;
143
+ }
144
+
127
145
$ this ->writable = false ;
128
146
$ this ->close ();
129
147
}
@@ -136,25 +154,18 @@ public function close()
136
154
137
155
$ this ->closing = true ;
138
156
$ this ->writable = false ;
157
+ $ this ->buffer = '' ;
139
158
140
159
if ($ this ->streaming !== null ) {
141
- // input stream ended but we were still streaming an entry => emit error about incomplete entry
142
- $ this ->streaming ->emit ('error ' , array ());
160
+ // input stream ended but we were still streaming an entry => forcefully close without error
143
161
$ this ->streaming ->close ();
144
162
$ this ->streaming = null ;
145
-
146
- $ this ->emit ('error ' , array ());
147
- }
148
-
149
- if ($ this ->buffer !== '' ) {
150
- // incomplete entry in buffer
151
- $ this ->emit ('error ' , array ());
152
- $ this ->buffer = '' ;
153
163
}
154
164
155
165
// ignore whether we're still expecting NUL-padding
156
166
157
- $ this ->emit ('close ' , array ($ this ));
167
+ $ this ->emit ('close ' );
168
+ $ this ->removeAllListeners ();
158
169
}
159
170
160
171
public function isWritable ()
@@ -173,11 +184,11 @@ private function consumeEntry($buffer)
173
184
$ this ->remaining -= $ len ;
174
185
175
186
// emit chunk of data
176
- $ this ->streaming ->emit ( ' data ' , array ( $ data, $ this -> streaming ) );
187
+ $ this ->streaming ->write ( $ data );
177
188
178
189
// nothing remaining => entry stream finished
179
190
if ($ this ->remaining === 0 ) {
180
- $ this ->streaming ->close ();
191
+ $ this ->streaming ->end ();
181
192
$ this ->streaming = null ;
182
193
}
183
194
0 commit comments