Passed
Push — 1.0.0 ( bca153...5c7ec9 )
by Zaahid
03:20
created

PartStream::parseOpenPath()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 11
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 11
ccs 10
cts 10
cp 1
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 8
nc 2
nop 4
crap 3
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
     * @param string $path The requested path
111
     * @param string $mode The requested open mode
112
     * @param int $options Additional streams API flags
113
     * @param string $opened_path The full path of the opened resource
114
     * @return boolean true if the resource was opened successfully
115
     */
116 5
    public function stream_open($path, $mode, $options, &$opened_path)
0 ignored issues
show
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...
117
    {
118 5
        $this->position = 0;
119 5
        $this->parseOpenPath($path, $this->id, $this->start, $this->end);
120 5
        $this->handle = $this->registry->get($this->id);
121 5
        $this->registry->increaseHandleRefCount($this->id);
122 5
        if ($mode !== 'r') {
123
            if ($options & STREAM_REPORT_ERRORS !== 0) {
124
                trigger_error('Invalid open mode: only read supported');
125
            }
126
            return false;
127
        }
128 5
        return ($this->handle !== null && $this->start !== null && $this->end !== null);
129
    }
130
    
131
    /**
132
     * Decreases the ref count for the underlying resource handle, which allows
133
     * the PartStreamRegistry to close it once no more references to it exist.
134
     */
135 5
    public function stream_close()
136
    {
137 5
        $this->registry->decreaseHandleRefCount($this->id);
138 5
    }
139
    
140
    /**
141
     * Reads up to $count characters from the stream and returns them.
142
     * 
143
     * @param int $count
144
     * @return string
145
     */
146 5
    public function stream_read($count)
147
    {
148 5
        $pos = ftell($this->handle);
149 5
        fseek($this->handle, $this->start + $this->position);
150 5
        $max = $this->end - ($this->start + $this->position);
151 5
        $nRead = min($count, $max);
152 5
        $ret = '';
153 5
        if ($nRead > 0) {
154 5
            $ret = fread($this->handle, $nRead);
155 5
        }
156 5
        $this->position += mb_strlen($ret, '8bit');
157 5
        fseek($this->handle, $pos);
158 5
        return $ret;
159
    }
160
    
161
    /**
162
     * Returns the current read position.
163
     * 
164
     * @return int
165
     */
166 1
    public function stream_tell()
167
    {
168 1
        return $this->position;
169
    }
170
    
171
    /**
172
     * Returns true if the end of the stream has been reached.
173
     * 
174
     * @return boolean
175
     */
176 5
    public function stream_eof()
177
    {
178 5
        if ($this->position + $this->start >= $this->end) {
179 5
            return true;
180
        }
181 1
        return false;
182
    }
183
    
184
    /**
185
     * Checks if the position is valid and seeks to it by setting
186
     * $this->position
187
     * 
188
     * @param int $pos
189
     * @return boolean true if set
190
     */
191 1
    private function streamSeekSet($pos)
192
    {
193 1
        if ($pos + $this->start < $this->end && $pos >= 0) {
194 1
            $this->position = $pos;
195 1
            return true;
196
        }
197 1
        return false;
198
    }
199
    
200
    /**
201
     * Moves the pointer to the given offset, in accordance to $whence.
202
     * 
203
     * @param int $offset
204
     * @param int $whence One of SEEK_SET, SEEK_CUR and SEEK_END.
205
     * @return boolean
206
     */
207 1
    public function stream_seek($offset, $whence = SEEK_SET)
208
    {
209 1
        $pos = $offset;
210
        switch ($whence) {
211 1
            case SEEK_CUR:
212
                // @codeCoverageIgnoreStart
213
                // this seems to be calculated for me in my version of PHP (5.5.9)
214
                $pos = $this->position + $offset;
215
                break;
216
                // @codeCoverageIgnoreEnd
217 1
            case SEEK_END:
218 1
                $pos = ($this->end - $this->start) + $offset;
219 1
                break;
220 1
            default:
221 1
                break;
222 1
        }
223 1
        return $this->streamSeekSet($pos);
224
    }
225
    
226
    /**
227
     * Returns information about the opened stream, as would be expected by
228
     * fstat.
229
     * 
230
     * @return array
231
     */
232 5
    public function stream_stat()
233
    {
234 5
        $arr = fstat($this->handle);
235 5
        if (!empty($arr['size'])) {
236 5
            $arr['size'] = $this->end - $this->start;
237 5
        }
238 5
        return $arr;
239
    }
240
}
241