Completed
Push — master ( 69e914...42b3e6 )
by Zaahid
02:52
created

PartStream   A

Complexity

Total Complexity 21

Size/Duplication

Total Lines 191
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Importance

Changes 5
Bugs 2 Features 0
Metric Value
wmc 21
c 5
b 2
f 0
lcom 1
cbo 2
dl 0
loc 191
rs 10

8 Methods

Rating   Name   Duplication   Size   Complexity  
A stream_tell() 0 4 1
A __construct() 0 5 1
B stream_open() 0 22 6
A stream_read() 0 14 2
A stream_eof() 0 7 3
A streamSeekSet() 0 8 3
A stream_seek() 0 15 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;
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
     * Called in response to fopen, file_get_contents, etc... with a
70
     * PartStream::STREAM_WRAPPER_PROTOCOL, e.g.,
71
     * fopen('mmp-mime-message://...');
72
     * 
73
     * The \ZBateson\MailMimeParser\Message object ID must be passed as the
74
     * 'host' part in $path.  The start and end boundaries of the part must be
75
     * passed as query string parameters in the path, for example:
76
     * 
77
     * fopen('mmp-mime-message://123456?start=0&end=20');
78
     * 
79
     * This would open a file handle to a MIME message with the ID 123456, with
80
     * a start offset of 0, and an end offset of 20.
81
     * 
82
     * TODO: $mode is not validated, although only read operations are
83
     * implemented in PartStream.  $options are not checked for error reporting
84
     * mode.
85
     * 
86
     * @param string $path The requested path
87
     * @param string $mode The requested open mode
88
     * @param int $options Additional streams API flags
89
     * @param string $opened_path The full path of the opened resource
90
     * @return boolean true if the resource was opened successfully
91
     */
92
    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...
93
    {
94
        $parts = parse_url($path);
95
        if (empty($parts['host']) || empty($parts['query'])) {
96
            return false;
97
        }
98
        
99
        $vars = [];
100
        parse_str($parts['query'], $vars);
101
        
102
        $id = $parts['host'];
103
        $handle = $this->registry->get($id);
104
        if ($handle === null || !isset($vars['start']) || !isset($vars['end'])) {
105
            return false;
106
        }
107
        
108
        $this->position = 0;
109
        $this->handle = $handle;
110
        $this->start = $vars['start'];
111
        $this->end = $vars['end'];
112
        return true;
113
    }
114
    
115
    /**
116
     * Reads up to $count characters from the stream and returns them.
117
     * 
118
     * @param int $count
119
     * @return string
120
     */
121
    public function stream_read($count)
122
    {
123
        $pos = ftell($this->handle);
124
        fseek($this->handle, $this->start + $this->position);
125
        $max = $this->end - ($this->start + $this->position);
126
        $nRead = min($count, $max);
127
        $ret = '';
128
        if ($nRead > 0) {
129
            $ret = fread($this->handle, $nRead);
130
        }
131
        $this->position += strlen($ret);
132
        fseek($this->handle, $pos);
133
        return $ret;
134
    }
135
    
136
    /**
137
     * Returns the current read position.
138
     * 
139
     * @return int
140
     */
141
    public function stream_tell()
142
    {
143
        return $this->position;
144
    }
145
    
146
    /**
147
     * Returns true if the end of the stream has been reached.
148
     * 
149
     * @return boolean
150
     */
151
    public function stream_eof()
152
    {
153
        if (feof($this->handle) || $this->position + $this->start >= $this->end) {
154
            return true;
155
        }
156
        return false;
157
    }
158
    
159
    /**
160
     * Checks if the position is valid and seeks to it by setting
161
     * $this->position
162
     * 
163
     * @param int $pos
164
     * @return boolean true if set
165
     */
166
    private function streamSeekSet($pos)
167
    {
168
        if ($pos + $this->start < $this->end && $pos >= 0) {
169
            $this->position = $pos;
170
            return true;
171
        }
172
        return false;
173
    }
174
    
175
    /**
176
     * Moves the pointer to the given offset, in accordance to $whence.
177
     * 
178
     * @param int $offset
179
     * @param int $whence One of SEEK_SET, SEEK_CUR and SEEK_END.
180
     * @return boolean
181
     */
182
    public function stream_seek($offset, $whence = SEEK_SET)
183
    {
184
        $pos = $offset;
185
        switch ($whence) {
186
            case SEEK_CUR:
187
                $pos = $this->position + $offset;
188
                break;
189
            case SEEK_END:
190
                $pos = ($this->end - $this->start) + $offset;
191
                break;
192
            default:
193
                break;
194
        }
195
        return $this->streamSeekSet($pos);
196
    }
197
    
198
    /**
199
     * Returns information about the opened stream, as would be expected by
200
     * fstat.
201
     * 
202
     * @return array
203
     */
204
    public function stream_stat()
205
    {
206
        $arr = fstat($this->handle);
207
        if (!empty($arr['size'])) {
208
            $arr['size'] = $this->end - $this->start;
209
        }
210
        return $arr;
211
    }
212
}
213