Completed
Push — master ( c7cf7f...702f4a )
by Zaahid
02:37
created

PartStream::parseOpenPath()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 11
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

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