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