Completed
Push — 5.x ( 831ff0...27843f )
by Lars
15:22 queued 08:41
created

readBytes()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 19
Code Lines 11

Duplication

Lines 6
Ratio 31.58 %

Code Coverage

Tests 12
CRAP Score 4
Metric Value
dl 6
loc 19
ccs 12
cts 12
cp 1
rs 9.2
cc 4
eloc 11
nc 4
nop 1
crap 4
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 48
    public function __construct(Swift_CharacterReaderFactory $factory, $charset)
81
    {
82 48
        self::_initializeMaps();
83 48
        $this->setCharacterReaderFactory($factory);
84 48
        $this->setCharacterSet($charset);
85 48
    }
86
87
    /**
88
     * Set the character set used in this CharacterStream.
89
     *
90
     * @param string $charset
91
     */
92 48
    public function setCharacterSet($charset)
93
    {
94 48
        $this->_charset = $charset;
95 48
        $this->_charReader = null;
96 48
    }
97
98
    /**
99
     * Set the CharacterReaderFactory for multi charset support.
100
     *
101
     * @param Swift_CharacterReaderFactory $factory
102
     */
103 48
    public function setCharacterReaderFactory(Swift_CharacterReaderFactory $factory)
104
    {
105 48
        $this->_charReaderFactory = $factory;
106 48
    }
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 2
            $c = array();
122
123 2 View Code Duplication
            for ($i = 0, $len = strlen($bytes); $i < $len; ++$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...
124 2
                $c[] = self::$_byteMap[$bytes[$i]];
125 2
            }
126
127 2
            $size = count($c);
128 2
            $need = $this->_charReader->validateByteSequence($c, $size);
129 2
            if ($need > 0 &&
130 2
                false !== $bytes = $os->read($need)) {
131 2 View Code Duplication
                for ($i = 0, $len = strlen($bytes); $i < $len; ++$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...
132 2
                    $c[] = self::$_byteMap[$bytes[$i]];
133 2
                }
134 2
            }
135
136 2
            $this->_array[] = $c;
137 2
            ++$this->_array_size;
138 2
        }
139 2
    }
140
141
    /**
142
     * Import a string a bytes into this CharacterStream, overwriting any existing
143
     * data in the stream.
144
     *
145
     * @param string $string
146
     */
147 17
    public function importString($string)
148
    {
149 17
        $this->flushContents();
150 17
        $this->write($string);
151 17
    }
152
153
    /**
154
     * Read $length characters from the stream and move the internal pointer
155
     * $length further into the stream.
156
     *
157
     * @param int $length
158
     *
159
     * @return false|string false on error
160
     */
161 7
    public function read($length)
162
    {
163 7
        if ($this->_offset == $this->_array_size) {
164 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...
165
        }
166
167
        // Don't use array slice
168 6
        $arrays = array();
169 6
        $end = $length + $this->_offset;
170 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...
171 6
            if (!isset($this->_array[$i])) {
172 2
                break;
173
            }
174 6
            $arrays[] = $this->_array[$i];
175 6
        }
176
177 6
        $this->_offset += $i - $this->_offset; // Limit function calls
178
179 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 184 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...
180 6
        foreach ($arrays as $array) {
181 6
            $chars .= implode('', array_map('chr', $array));
182 6
        }
183
184 6
        return $chars;
185
    }
186
187
    /**
188
     * Read $length characters from the stream and return a 1-dimensional array
189
     * containing there octet values.
190
     *
191
     * @param int $length
192
     *
193
     * @return integer[]
194
     */
195 9
    public function readBytes($length)
196
    {
197 9
        if ($this->_offset == $this->_array_size) {
198 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...
199
        }
200
201 9
        $arrays = array();
202 9
        $end = $length + $this->_offset;
203 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...
204 9
            if (!isset($this->_array[$i])) {
205 8
                break;
206
            }
207 9
            $arrays[] = $this->_array[$i];
208 9
        }
209
210 9
        $this->_offset += ($i - $this->_offset); // Limit function calls
211
212 9
        return call_user_func_array('array_merge', $arrays);
213
    }
214
215
    /**
216
     * Write $chars to the end of the stream.
217
     *
218
     * @param string $chars
219
     */
220 17
    public function write($chars)
221
    {
222 17
        if (!isset($this->_charReader)) {
223 17
            $this->_charReader = $this->_charReaderFactory->getReaderFor($this->_charset);
224 17
        }
225
226 17
        $startLength = $this->_charReader->getInitialByteSize();
227
228 17
        $fp = fopen('php://memory', 'w+b');
229 17
        fwrite($fp, $chars);
230 17
        unset($chars);
231 17
        fseek($fp, 0, SEEK_SET);
232
233 17
        $buffer = array(0);
234 17
        $buf_pos = 1;
235 17
        $buf_len = 1;
236 17
        $has_datas = true;
237
        do {
238 17
            $bytes = array();
239
            // Buffer Filing
240 17
            if ($buf_len - $buf_pos < $startLength) {
241 17
                $buf = array_splice($buffer, $buf_pos);
242 17
                $new = $this->_reloadBuffer($fp, 100);
243 17
                if ($new) {
244 17
                    $buffer = array_merge($buf, $new);
245 17
                    $buf_len = count($buffer);
246 17
                    $buf_pos = 0;
247 17
                } else {
248 17
                    $has_datas = false;
249
                }
250 17
            }
251 17
            if ($buf_len - $buf_pos > 0) {
252 17
                $size = 0;
253 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...
254 17
                    ++$size;
255 17
                    $bytes[] = $buffer[$buf_pos++];
256 17
                }
257 17
                $need = $this->_charReader->validateByteSequence(
258 17
                    $bytes, $size);
259 17
                if ($need > 0) {
260 11
                    if ($buf_len - $buf_pos < $need) {
261 2
                        $new = $this->_reloadBuffer($fp, $need);
262
263 2
                        if ($new) {
264 2
                            $buffer = array_merge($buffer, $new);
265 2
                            $buf_len = count($buffer);
266 2
                        }
267 2
                    }
268 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...
269 11
                        $bytes[] = $buffer[$buf_pos++];
270 11
                    }
271 11
                }
272 17
                $this->_array[] = $bytes;
273 17
                ++$this->_array_size;
274 17
            }
275 17
        } while ($has_datas);
276
277 17
        fclose($fp);
278 17
    }
279
280
    /**
281
     * Move the internal pointer to $charOffset in the stream.
282
     *
283
     * @param int $charOffset
284
     */
285 1
    public function setPointer($charOffset)
286
    {
287 1
        if ($charOffset > $this->_array_size) {
288
            $charOffset = $this->_array_size;
289 1
        } elseif ($charOffset < 0) {
290
            $charOffset = 0;
291
        }
292 1
        $this->_offset = $charOffset;
293 1
    }
294
295
    /**
296
     * Empty the stream and reset the internal pointer.
297
     */
298 17
    public function flushContents()
299
    {
300 17
        $this->_offset = 0;
301 17
        $this->_array = array();
302 17
        $this->_array_size = 0;
303 17
    }
304
305
    /**
306
     * @param resource $fp
307
     * @param int      $len
308
     *
309
     * @return array|false false on error
310
     */
311 17
    private function _reloadBuffer($fp, $len)
312
    {
313 17
        if (!feof($fp) && ($bytes = fread($fp, $len)) !== false) {
314 17
            $buf = array();
315 17 View Code Duplication
            for ($i = 0, $len = strlen($bytes); $i < $len; ++$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...
316 17
                $buf[] = self::$_byteMap[$bytes[$i]];
317 17
            }
318
319 17
            return $buf;
320
        }
321
322 17
        return false;
323
    }
324
325 48
    private static function _initializeMaps()
326
    {
327 48
        if (!isset(self::$_charMap)) {
328 1
            self::$_charMap = array();
329 1
            for ($byte = 0; $byte < 256; ++$byte) {
330 1
                self::$_charMap[$byte] = chr($byte);
331 1
            }
332 1
            self::$_byteMap = array_flip(self::$_charMap);
333 1
        }
334 48
    }
335
}
336