Passed
Push — master ( f41556...5b26a6 )
by Zaahid
13:23
created

ChunkSplitStream   A

Complexity

Total Complexity 9

Size/Duplication

Total Lines 102
Duplicated Lines 0 %

Test Coverage

Coverage 86.21%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 30
c 1
b 0
f 0
dl 0
loc 102
ccs 25
cts 29
cp 0.8621
rs 10
wmc 9

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 7 1
A close() 0 4 1
A beforeClose() 0 4 2
A write() 0 5 1
A detach() 0 6 1
A getChunkedString() 0 13 3
1
<?php
2
/**
3
 * This file is part of the ZBateson\StreamDecorators project.
4
 *
5
 * @license http://opensource.org/licenses/bsd-license.php BSD
6
 */
7
8
namespace ZBateson\StreamDecorators;
9
10
use GuzzleHttp\Psr7\StreamDecoratorTrait;
11
use Psr\Http\Message\StreamInterface;
12
13
/**
14
 * Inserts line ending characters after the set number of characters have been
15
 * written to the underlying stream.
16
 *
17
 * @author Zaahid Bateson
18
 */
19
class ChunkSplitStream implements StreamInterface
20
{
21
    use StreamDecoratorTrait;
22
23
    /**
24
     * @var int Number of bytes written, and importantly, if non-zero, writes a
25
     *      final $lineEnding on close (and so maintained instead of using
26
     *      tell() directly)
27
     */
28
    private int $position;
29
30
    /**
31
     * @var int The number of characters in a line before inserting $lineEnding.
32
     */
33
    private int $lineLength;
34
35
    /**
36
     * @var string The line ending characters to insert.
37
     */
38
    private string $lineEnding;
39
40
    /**
41
     * @var int The strlen() of $lineEnding
42
     */
43
    private int $lineEndingLength;
44
45
    /**
46
     * @var StreamInterface $stream
47
     */
48
    private StreamInterface $stream;
49
50 2
    public function __construct(StreamInterface $stream, int $lineLength = 76, string $lineEnding = "\r\n")
51
    {
52 2
        $this->stream = $stream;
53 2
        $this->position = 0;
54 2
        $this->lineLength = $lineLength;
55 2
        $this->lineEnding = $lineEnding;
56 2
        $this->lineEndingLength = \strlen($this->lineEnding);
57
    }
58
59
    /**
60
     * Inserts the line ending character after each line length characters in
61
     * the passed string, making sure previously written bytes are taken into
62
     * account.
63
     */
64 2
    private function getChunkedString(string $string) : string
65
    {
66 2
        $firstLine = '';
67 2
        if ($this->tell() !== 0) {
68 2
            $next = $this->lineLength - ($this->position % ($this->lineLength + $this->lineEndingLength));
69 2
            if (\strlen($string) > $next) {
70 2
                $firstLine = \substr($string, 0, $next) . $this->lineEnding;
71 2
                $string = \substr($string, $next);
72
            }
73
        }
74
        // chunk_split always ends with the passed line ending
75 2
        $chunked = $firstLine . \chunk_split($string, $this->lineLength, $this->lineEnding);
76 2
        return \substr($chunked, 0, \strlen($chunked) - $this->lineEndingLength);
77
    }
78
79
    /**
80
     * Writes the passed string to the underlying stream, ensuring line endings
81
     * are inserted every "line length" characters in the string.
82
     *
83
     * @param string $string
84
     * @return int number of bytes written
85
     */
86 2
    public function write($string) : int
87
    {
88 2
        $chunked = $this->getChunkedString($string);
89 2
        $this->position += \strlen($chunked);
90 2
        return $this->stream->write($chunked);
91
    }
92
93
    /**
94
     * Inserts a final line ending character.
95
     */
96 2
    private function beforeClose() : void
97
    {
98 2
        if ($this->position !== 0) {
99 2
            $this->stream->write($this->lineEnding);
100
        }
101
    }
102
103
    /**
104
     * @inheritDoc
105
     */
106 2
    public function close() : void
107
    {
108 2
        $this->beforeClose();
109 2
        $this->stream->close();
110
    }
111
112
    /**
113
     * @inheritDoc
114
     */
115
    public function detach()
116
    {
117
        $this->beforeClose();
118
        $this->stream->detach();
119
120
        return null;
121
    }
122
}
123