Completed
Push — 5.x ( 02fa24...554335 )
by Lars
12:30 queued 06:50
created

_reloadBuffer()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 17
Code Lines 10

Duplication

Lines 9
Ratio 52.94 %

Code Coverage

Tests 11
CRAP Score 4.0092

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 9
loc 17
ccs 11
cts 12
cp 0.9167
rs 9.2
cc 4
eloc 10
nc 4
nop 2
crap 4.0092
1
<?php
2
3
/*
4
 * This file is part of SwiftMailer.
5
 * (c) 2004-2009 Chris Corbyn
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
/**
12
 * A CharacterStream implementation which stores characters in an internal array.
13
 *
14
 * @author Chris Corbyn
15
 */
16
class Swift_CharacterStream_ArrayCharacterStream implements Swift_CharacterStream
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
17
{
18
    /**
19
     * A map of byte values and their respective characters
20
     *
21
     * @var array
22
     */
23
    private static $_charMap;
24
25
    /**
26
     * A map of characters and their derivative byte values
27
     *
28
     * @var array
29
     */
30
    private static $_byteMap;
31
32
    /**
33
     * The char reader (lazy-loaded) for the current charset
34
     *
35
     * @var Swift_CharacterReader
36
     */
37
    private $_charReader;
38
39
    /**
40
     * A factory for creating CharacterReader instance
41
     *
42
     * @var Swift_CharacterReaderFactory
43
     */
44
    private $_charReaderFactory;
45
46
    /**
47
     * The character set this stream is using
48
     *
49
     * @var string
50
     */
51
    private $_charset;
52
53
    /**
54
     * Array of characters
55
     *
56
     * @var array
57
     */
58
    private $_array = array();
59
60
    /**
61
     * Size of the array of character
62
     *
63
     * @var int
64
     */
65
    private $_array_size = 0;
66
67
    /**
68
     * The current character offset in the stream
69
     *
70
     * @var int
71
     */
72
    private $_offset = 0;
73
74
    /**
75
     * Create a new CharacterStream with the given $chars, if set.
76
     *
77
     * @param Swift_CharacterReaderFactory $factory for loading validators
78
     * @param string                       $charset used in the stream
79
     */
80 54
    public function __construct(Swift_CharacterReaderFactory $factory, $charset)
81
    {
82 54
        self::_initializeMaps();
83 54
        $this->setCharacterReaderFactory($factory);
84 54
        $this->setCharacterSet($charset);
85 54
    }
86
87
    /**
88
     * Set the character set used in this CharacterStream.
89
     *
90
     * @param string $charset
91
     */
92 54
    public function setCharacterSet($charset)
93
    {
94 54
        $this->_charset = $charset;
95 54
        $this->_charReader = null;
96 54
    }
97
98
    /**
99
     * Set the CharacterReaderFactory for multi charset support.
100
     *
101
     * @param Swift_CharacterReaderFactory $factory
102
     */
103 54
    public function setCharacterReaderFactory(Swift_CharacterReaderFactory $factory)
104
    {
105 54
        $this->_charReaderFactory = $factory;
106 54
    }
107
108
    /**
109
     * Overwrite this character stream using the byte sequence in the byte stream.
110
     *
111
     * @param Swift_OutputByteStream $os output stream to read from
112
     */
113 2
    public function importByteStream(Swift_OutputByteStream $os)
114
    {
115 2
        if (!isset($this->_charReader)) {
116 2
            $this->_charReader = $this->_charReaderFactory->getReaderFor($this->_charset);
117 2
        }
118
119 2
        $startLength = $this->_charReader->getInitialByteSize();
120 2
        while (false !== $bytes = $os->read($startLength)) {
121
122 2
            $c = array();
123 2
            $len = strlen($bytes);
124 2
            for ($i = 0; $i < $len; ++$i) {
125 2
                $c[] = self::$_byteMap[$bytes[$i]];
126 2
            }
127
128 2
            $size = count($c);
129 2
            $need = $this->_charReader->validateByteSequence($c, $size);
130 2
            if ($need > 0) {
131 2
                $bytes = $os->read($need);
132 2 View Code Duplication
                if (false !== $bytes) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
133 2
                    $len = strlen($bytes);
134 2
                    for ($i = 0; $i < $len; ++$i) {
135 2
                        $c[] = self::$_byteMap[$bytes[$i]];
136 2
                    }
137 2
                }
138 2
            }
139
140 2
            $this->_array[] = $c;
141 2
            ++$this->_array_size;
142 2
        }
143 2
    }
144
145
    /**
146
     * Import a string a bytes into this CharacterStream, overwriting any existing
147
     * data in the stream.
148
     *
149
     * @param string $string
150
     */
151 17
    public function importString($string)
152
    {
153 17
        $this->flushContents();
154 17
        $this->write($string);
155 17
    }
156
157
    /**
158
     * Read $length characters from the stream and move the internal pointer
159
     * $length further into the stream.
160
     *
161
     * @param int $length
162
     *
163
     * @return false|string false on error
164
     */
165 7
    public function read($length)
166
    {
167 7
        if ($this->_offset === $this->_array_size) {
168 6
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type declared by the interface Swift_CharacterStream::read of type string.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
169
        }
170
171
        // Don't use array slice
172 6
        $arrays = array();
173 6
        $end = $length + $this->_offset;
174 6 View Code Duplication
        for ($i = $this->_offset; $i < $end; ++$i) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
175 6
            if (!isset($this->_array[$i])) {
176 2
                break;
177
            }
178 6
            $arrays[] = $this->_array[$i];
179 6
        }
180
181 6
        $this->_offset += $i - $this->_offset; // Limit function calls
182
183 6
        $chars = false;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The expression false; of type false adds false to the return on line 188 which is incompatible with the return type declared by the interface Swift_CharacterStream::read of type string. It seems like you forgot to handle an error condition.
Loading history...
184 6
        foreach ($arrays as $array) {
185 6
            $chars .= implode('', array_map('chr', $array));
186 6
        }
187
188 6
        return $chars;
189
    }
190
191
    /**
192
     * Read $length characters from the stream and return a 1-dimensional array
193
     * containing there octet values.
194
     *
195
     * @param int $length
196
     *
197
     * @return integer[]
198
     */
199 9
    public function readBytes($length)
200
    {
201 9
        if ($this->_offset === $this->_array_size) {
202 9
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type declared by the interface Swift_CharacterStream::readBytes of type integer[].

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
203
        }
204
205 9
        $arrays = array();
206 9
        $end = $length + $this->_offset;
207 9 View Code Duplication
        for ($i = $this->_offset; $i < $end; ++$i) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
208 9
            if (!isset($this->_array[$i])) {
209 8
                break;
210
            }
211 9
            $arrays[] = $this->_array[$i];
212 9
        }
213
214 9
        $this->_offset += ($i - $this->_offset); // Limit function calls
215
216 9
        return call_user_func_array('array_merge', $arrays);
217
    }
218
219
    /**
220
     * Write $chars to the end of the stream.
221
     *
222
     * @param string $chars
223
     */
224 17
    public function write($chars)
225
    {
226 17
        if (!isset($this->_charReader)) {
227 17
            $this->_charReader = $this->_charReaderFactory->getReaderFor($this->_charset);
228 17
        }
229
230 17
        $startLength = $this->_charReader->getInitialByteSize();
231
232 17
        $fp = fopen('php://memory', 'w+b');
233 17
        fwrite($fp, $chars);
234 17
        unset($chars);
235 17
        fseek($fp, 0, SEEK_SET);
236
237 17
        $buffer = array(0);
238 17
        $buf_pos = 1;
239 17
        $buf_len = 1;
240 17
        $has_data = true;
241
        do {
242 17
            $bytes = array();
243
            // Buffer Filing
244 17
            if ($buf_len - $buf_pos < $startLength) {
245 17
                $buf = array_splice($buffer, $buf_pos);
246 17
                $new = $this->_reloadBuffer($fp, 100);
247 17
                if ($new) {
248 17
                    $buffer = array_merge($buf, $new);
249 17
                    $buf_len = count($buffer);
250 17
                    $buf_pos = 0;
251 17
                } else {
252 17
                    $has_data = false;
253
                }
254 17
            }
255 17
            if ($buf_len - $buf_pos > 0) {
256 17
                $size = 0;
257 17 View Code Duplication
                for ($i = 0; $i < $startLength && isset($buffer[$buf_pos]); ++$i) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
258 17
                    ++$size;
259 17
                    $bytes[] = $buffer[$buf_pos++];
260 17
                }
261 17
                $need = $this->_charReader->validateByteSequence(
262 17
                    $bytes, $size
263 17
                );
264 17
                if ($need > 0) {
265 11
                    if ($buf_len - $buf_pos < $need) {
266 2
                        $new = $this->_reloadBuffer($fp, $need);
267
268 2
                        if ($new) {
269 2
                            $buffer = array_merge($buffer, $new);
270 2
                            $buf_len = count($buffer);
271 2
                        }
272 2
                    }
273 11 View Code Duplication
                    for ($i = 0; $i < $need && isset($buffer[$buf_pos]); ++$i) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
274 11
                        $bytes[] = $buffer[$buf_pos++];
275 11
                    }
276 11
                }
277 17
                $this->_array[] = $bytes;
278 17
                ++$this->_array_size;
279 17
            }
280 17
        } while ($has_data);
281
282 17
        fclose($fp);
283 17
    }
284
285
    /**
286
     * Move the internal pointer to $charOffset in the stream.
287
     *
288
     * @param int $charOffset
289
     */
290 1
    public function setPointer($charOffset)
291
    {
292 1
        if ($charOffset > $this->_array_size) {
293
            $charOffset = $this->_array_size;
294 1
        } elseif ($charOffset < 0) {
295
            $charOffset = 0;
296
        }
297 1
        $this->_offset = $charOffset;
298 1
    }
299
300
    /**
301
     * Empty the stream and reset the internal pointer.
302
     */
303 17
    public function flushContents()
304
    {
305 17
        $this->_offset = 0;
306 17
        $this->_array = array();
307 17
        $this->_array_size = 0;
308 17
    }
309
310
    /**
311
     * @param resource $fp
312
     * @param int      $len
313
     *
314
     * @return array|false false on error
315
     */
316 17
    private function _reloadBuffer($fp, $len)
317
    {
318 17
        if (!feof($fp)) {
319 17
            $bytes = fread($fp, $len);
320 17 View Code Duplication
            if ($bytes !== false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
321 17
                $buf = array();
322 17
                $len = strlen($bytes);
323 17
                for ($i = 0; $i < $len; ++$i) {
324 17
                    $buf[] = self::$_byteMap[$bytes[$i]];
325 17
                }
326
327 17
                return $buf;
328
            }
329
        }
330
331 17
        return false;
332
    }
333
334 54
    private static function _initializeMaps()
335
    {
336 54
        if (!isset(self::$_charMap)) {
337 1
            self::$_charMap = array();
338 1
            for ($byte = 0; $byte < 256; ++$byte) {
339 1
                self::$_charMap[$byte] = chr($byte);
340 1
            }
341 1
            self::$_byteMap = array_flip(self::$_charMap);
342 1
        }
343 54
    }
344
}
345