Completed
Push — master ( ab502e...07a33d )
by Zaahid
06:14
created

PartStream   A

Complexity

Total Complexity 21

Size/Duplication

Total Lines 215
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Test Coverage

Coverage 100%

Importance

Changes 3
Bugs 0 Features 2
Metric Value
wmc 21
c 3
b 0
f 2
lcom 1
cbo 2
dl 0
loc 215
ccs 62
cts 62
cp 1
rs 10

10 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A parseOpenPath() 0 11 3
A stream_open() 0 8 3
A stream_close() 0 4 1
A stream_read() 0 14 2
A stream_tell() 0 4 1
A stream_eof() 0 7 2
A streamSeekSet() 0 8 3
A stream_seek() 0 18 3
A stream_stat() 0 8 2
1
<?php
2
/**
3
 * This file is part of the ZBateson\MailMimeParser project.
4
 *
5
 * @license http://opensource.org/licenses/bsd-license.php BSD
6
 */
7
namespace ZBateson\MailMimeParser\Stream;
8
9
use ZBateson\MailMimeParser\SimpleDi;
10
11
/**
12
 * Implementation of a stream wrapper representing content for a specific MIME
13
 * part of an email message.
14
 * 
15
 * Basically defines character boundaries for a "parent" stream - namely the
16
 * main stream for a message - where read operations are not permitted beyond
17
 * the character boundaries of a specific part.  The boundaries are parsed from
18
 * parameters passed as the "path" to stream_open (with fopen, etc...)
19
 * 
20
 * Note that only read operations are permitted.
21
 *
22
 * @author Zaahid Bateson
23
 */
24
class PartStream
25
{
26
    /**
27
     * The protocol name used to register the stream with
28
     * stream_wrapper_register
29
     */
30
    const STREAM_WRAPPER_PROTOCOL = 'mmp-mime-message';
31
    
32
    /**
33
     * @var int the message ID this PartStream belongs to
34
     */
35
    protected $id;
36
    
37
    /**
38
     * @var resource The resource handle for the opened part.  Essentially the
39
     *      MIME message's stream handle.
40
     */
41
    protected $handle;
42
    
43
    /**
44
     * @var int The offset character position in $this->handle where the current
45
     *      mime part's content starts.
46
     */
47
    protected $start;
48
    
49
    /**
50
     * @var int The offset character position in $this->handle where the current
51
     *      mime part's content ends.
52
     */
53
    protected $end;
54
    
55
    /**
56
     * @var PartStreamRegistry The registry service object. 
57
     */
58
    protected $registry;
59
    
60
    /**
61
     * @var int the current read position.
62
     */
63
    private $position;
64
    
65
    /**
66
     * Constructs a PartStream.
67
     */
68 5
    public function __construct()
69
    {
70 5
        $di = SimpleDi::singleton();
71 5
        $this->registry = $di->getPartStreamRegistry();
72 5
    }
73
    
74
    /**
75
     * Extracts the PartStreamRegistry resource id, start, and end positions for
76
     * the passed path and assigns them to the passed-by-reference parameters
77
     * $id, $start and $end respectively.
78
     * 
79
     * @param string $path
80
     * @param string $id
81
     * @param int $start
82
     * @param int $end
83
     */
84 5
    private function parseOpenPath($path, &$id, &$start, &$end)
85
    {
86 5
        $vars = [];
87 5
        $parts = parse_url($path);
88 5
        if (!empty($parts['host']) && !empty($parts['query'])) {
89 5
            parse_str($parts['query'], $vars);
90 5
            $id = $parts['host'];
91 5
            $start = intval($vars['start']);
92 5
            $end = intval($vars['end']);
93 5
        }
94 5
    }
95
    
96
    /**
97
     * Called in response to fopen, file_get_contents, etc... with a
98
     * PartStream::STREAM_WRAPPER_PROTOCOL, e.g.,
99
     * fopen('mmp-mime-message://...');
100
     * 
101
     * The \ZBateson\MailMimeParser\Message object ID must be passed as the
102
     * 'host' part in $path.  The start and end boundaries of the part must be
103
     * passed as query string parameters in the path, for example:
104
     * 
105
     * fopen('mmp-mime-message://123456?start=0&end=20');
106
     * 
107
     * This would open a file handle to a MIME message with the ID 123456, with
108
     * a start offset of 0, and an end offset of 20.
109
     * 
110
     * TODO: $mode is not validated, although only read operations are
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
111
     * implemented in PartStream.  $options are not checked for error reporting
112
     * mode.
113
     * 
114
     * @param string $path The requested path
115
     * @param string $mode The requested open mode
116
     * @param int $options Additional streams API flags
117
     * @param string $opened_path The full path of the opened resource
118
     * @return boolean true if the resource was opened successfully
119
     */
120 5
    public function stream_open($path, $mode, $options, &$opened_path)
3 ignored issues
show
Unused Code introduced by
The parameter $mode is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $options is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $opened_path is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
121
    {
122 5
        $this->position = 0;
123 5
        $this->parseOpenPath($path, $this->id, $this->start, $this->end);
124 5
        $this->handle = $this->registry->get($this->id);
125 5
        $this->registry->increaseHandleRefCount($this->id);
126 5
        return ($this->handle !== null && $this->start !== null && $this->end !== null);
127
    }
128
    
129
    /**
130
     * Decreases the ref count for the underlying resource handle, which allows
131
     * the PartStreamRegistry to close it once no more references to it exist.
132
     */
133 5
    public function stream_close()
134
    {
135 5
        $this->registry->decreaseHandleRefCount($this->id);
136 5
    }
137
    
138
    /**
139
     * Reads up to $count characters from the stream and returns them.
140
     * 
141
     * @param int $count
142
     * @return string
143
     */
144 5
    public function stream_read($count)
145
    {
146 5
        $pos = ftell($this->handle);
147 5
        fseek($this->handle, $this->start + $this->position);
148 5
        $max = $this->end - ($this->start + $this->position);
149 5
        $nRead = min($count, $max);
150 5
        $ret = '';
151 5
        if ($nRead > 0) {
152 5
            $ret = fread($this->handle, $nRead);
153 5
        }
154 5
        $this->position += strlen($ret);
155 5
        fseek($this->handle, $pos);
156 5
        return $ret;
157
    }
158
    
159
    /**
160
     * Returns the current read position.
161
     * 
162
     * @return int
163
     */
164 1
    public function stream_tell()
165
    {
166 1
        return $this->position;
167
    }
168
    
169
    /**
170
     * Returns true if the end of the stream has been reached.
171
     * 
172
     * @return boolean
173
     */
174 5
    public function stream_eof()
175
    {
176 5
        if ($this->position + $this->start >= $this->end) {
177 5
            return true;
178
        }
179 1
        return false;
180
    }
181
    
182
    /**
183
     * Checks if the position is valid and seeks to it by setting
184
     * $this->position
185
     * 
186
     * @param int $pos
187
     * @return boolean true if set
188
     */
189 1
    private function streamSeekSet($pos)
190
    {
191 1
        if ($pos + $this->start < $this->end && $pos >= 0) {
192 1
            $this->position = $pos;
193 1
            return true;
194
        }
195 1
        return false;
196
    }
197
    
198
    /**
199
     * Moves the pointer to the given offset, in accordance to $whence.
200
     * 
201
     * @param int $offset
202
     * @param int $whence One of SEEK_SET, SEEK_CUR and SEEK_END.
203
     * @return boolean
204
     */
205 1
    public function stream_seek($offset, $whence = SEEK_SET)
206
    {
207 1
        $pos = $offset;
208
        switch ($whence) {
209 1
            case SEEK_CUR:
210
                // @codeCoverageIgnoreStart
211
                // this seems to be calculated for me in my version of PHP (5.5.9)
212
                $pos = $this->position + $offset;
213
                break;
214
                // @codeCoverageIgnoreEnd
215 1
            case SEEK_END:
216 1
                $pos = ($this->end - $this->start) + $offset;
217 1
                break;
218 1
            default:
219 1
                break;
220 1
        }
221 1
        return $this->streamSeekSet($pos);
222
    }
223
    
224
    /**
225
     * Returns information about the opened stream, as would be expected by
226
     * fstat.
227
     * 
228
     * @return array
229
     */
230 5
    public function stream_stat()
231
    {
232 5
        $arr = fstat($this->handle);
233 5
        if (!empty($arr['size'])) {
234 5
            $arr['size'] = $this->end - $this->start;
235 5
        }
236 5
        return $arr;
237
    }
238
}
239