Completed
Push — master ( 4041bf...487db1 )
by Zaahid
07:54
created

PartStream::parseOpenPath()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 11
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 11
rs 9.4285
cc 3
eloc 8
nc 2
nop 4
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 resource The resource handle for the opened part.  Essentially the
34
     *      MIME message's stream handle.
35
     */
36
    protected $handle;
37
    
38
    /**
39
     * @var int The offset character position in $this->handle where the current
40
     *      mime part's content starts.
41
     */
42
    protected $start;
43
    
44
    /**
45
     * @var int The offset character position in $this->handle where the current
46
     *      mime part's content ends.
47
     */
48
    protected $end;
49
    
50
    /**
51
     * @var PartStreamRegistry The registry service object. 
52
     */
53
    protected $registry;
54
    
55
    /**
56
     * @var int the current read position.
57
     */
58
    private $position;
59
    
60
    /**
61
     * Constructs a PartStream.
62
     */
63
    public function __construct()
64
    {
65
        $di = SimpleDi::singleton();
66
        $this->registry = $di->getPartStreamRegistry();
67
    }
68
    
69
    /**
70
     * Extracts the PartStreamRegistry resource id, start, and end positions for
71
     * the passed path and assigns them to the passed-by-reference parameters
72
     * $id, $start and $end respectively.
73
     * 
74
     * @param string $path
75
     * @param string $id
76
     * @param int $start
77
     * @param int $end
78
     */
79
    private function parseOpenPath($path, &$id, &$start, &$end)
80
    {
81
        $vars = [];
82
        $parts = parse_url($path);
83
        if (!empty($parts['host']) && !empty($parts['query'])) {
84
            parse_str($parts['query'], $vars);
85
            $id = $parts['host'];
86
            $start = intval($vars['start']);
87
            $end = intval($vars['end']);
88
        }
89
    }
90
    
91
    /**
92
     * Called in response to fopen, file_get_contents, etc... with a
93
     * PartStream::STREAM_WRAPPER_PROTOCOL, e.g.,
94
     * fopen('mmp-mime-message://...');
95
     * 
96
     * The \ZBateson\MailMimeParser\Message object ID must be passed as the
97
     * 'host' part in $path.  The start and end boundaries of the part must be
98
     * passed as query string parameters in the path, for example:
99
     * 
100
     * fopen('mmp-mime-message://123456?start=0&end=20');
101
     * 
102
     * This would open a file handle to a MIME message with the ID 123456, with
103
     * a start offset of 0, and an end offset of 20.
104
     * 
105
     * TODO: $mode is not validated, although only read operations are
106
     * implemented in PartStream.  $options are not checked for error reporting
107
     * mode.
108
     * 
109
     * @param string $path The requested path
110
     * @param string $mode The requested open mode
111
     * @param int $options Additional streams API flags
112
     * @param string $opened_path The full path of the opened resource
113
     * @return boolean true if the resource was opened successfully
114
     */
115
    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...
116
    {
117
        $id = '';
118
        $this->start = null;
119
        $this->end = null;
120
        $this->position = 0;
121
        $this->parseOpenPath($path, $id, $this->start, $this->end);
122
        $this->handle = $this->registry->get($id);
123
        return ($this->handle !== null && $this->start !== null && $this->end !== null);
124
    }
125
    
126
    /**
127
     * Reads up to $count characters from the stream and returns them.
128
     * 
129
     * @param int $count
130
     * @return string
131
     */
132
    public function stream_read($count)
133
    {
134
        $pos = ftell($this->handle);
135
        fseek($this->handle, $this->start + $this->position);
136
        $max = $this->end - ($this->start + $this->position);
137
        $nRead = min($count, $max);
138
        $ret = '';
139
        if ($nRead > 0) {
140
            $ret = fread($this->handle, $nRead);
141
        }
142
        $this->position += strlen($ret);
143
        fseek($this->handle, $pos);
144
        return $ret;
145
    }
146
    
147
    /**
148
     * Returns the current read position.
149
     * 
150
     * @return int
151
     */
152
    public function stream_tell()
153
    {
154
        return $this->position;
155
    }
156
    
157
    /**
158
     * Returns true if the end of the stream has been reached.
159
     * 
160
     * @return boolean
161
     */
162
    public function stream_eof()
163
    {
164
        if (feof($this->handle) || $this->position + $this->start >= $this->end) {
165
            return true;
166
        }
167
        return false;
168
    }
169
    
170
    /**
171
     * Checks if the position is valid and seeks to it by setting
172
     * $this->position
173
     * 
174
     * @param int $pos
175
     * @return boolean true if set
176
     */
177
    private function streamSeekSet($pos)
178
    {
179
        if ($pos + $this->start < $this->end && $pos >= 0) {
180
            $this->position = $pos;
181
            return true;
182
        }
183
        return false;
184
    }
185
    
186
    /**
187
     * Moves the pointer to the given offset, in accordance to $whence.
188
     * 
189
     * @param int $offset
190
     * @param int $whence One of SEEK_SET, SEEK_CUR and SEEK_END.
191
     * @return boolean
192
     */
193
    public function stream_seek($offset, $whence = SEEK_SET)
194
    {
195
        $pos = $offset;
196
        switch ($whence) {
197
            case SEEK_CUR:
198
                $pos = $this->position + $offset;
199
                break;
200
            case SEEK_END:
201
                $pos = ($this->end - $this->start) + $offset;
202
                break;
203
            default:
204
                break;
205
        }
206
        return $this->streamSeekSet($pos);
207
    }
208
    
209
    /**
210
     * Returns information about the opened stream, as would be expected by
211
     * fstat.
212
     * 
213
     * @return array
214
     */
215
    public function stream_stat()
216
    {
217
        $arr = fstat($this->handle);
218
        if (!empty($arr['size'])) {
219
            $arr['size'] = $this->end - $this->start;
220
        }
221
        return $arr;
222
    }
223
}
224