Completed
Pull Request — master (#12)
by Frederik
12:11
created

QuotedPrintableStream::isSeekable()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 0
cts 1
cp 0
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
crap 2
1
<?php
2
declare(strict_types=1);
3
4
namespace Genkgo\Mail\Stream;
5
6
use Genkgo\Mail\StreamInterface;
7
8
final class QuotedPrintableStream implements StreamInterface
9
{
10
    /**
11
     * @var StreamInterface
12
     */
13
    private $decoratedStream;
14
    /**
15
     * @var int
16
     */
17
    private $lineLength;
18
    /**
19
     * @var resource
20
     */
21
    private $filter;
22
    /**
23
     * @var string
24
     */
25
    private $lineBreak;
26
27
    /**
28
     * QuotedPrintableStream constructor.
29
     * @param resource $resource
30
     * @param int $lineLength
31
     * @param string $lineBreak
32
     */
33 12
    public function __construct($resource, int $lineLength = 75, string $lineBreak = "\r\n")
34
    {
35 12
        $this->decoratedStream = new ResourceStream($resource);
36 12
        $this->lineLength = $lineLength;
37 12
        $this->lineBreak = $lineBreak;
38
39 12
        $this->applyFilter();
40 12
    }
41
42
    /**
43
     * @param string $string
44
     * @param int $lineLength
45
     * @param string $lineBreak
46
     * @return QuotedPrintableStream
47
     */
48 11
    public static function fromString(string $string, int $lineLength = 75, string $lineBreak = "\r\n"): QuotedPrintableStream
49
    {
50 11
        $string = str_replace(
51 11
            ["\r\n", "\r", "\n", "\t\r\n", " \r\n"],
52 11
            ["\n", "\n", "\r\n", "\r\n", "\r\n"],
53 11
            $string
54
        );
55
56 11
        $resource = fopen('php://memory', 'r+');
57 11
        fwrite($resource, $string);
58 11
        return new self($resource, $lineLength, $lineBreak);
59
    }
60
61
    /**
62
     *
63
     */
64 12
    private function applyFilter(): void
65
    {
66 12
        $this->filter = stream_filter_prepend(
67 12
            $this->decoratedStream->detach(),
68 12
            'convert.quoted-printable-encode',
69 12
            STREAM_FILTER_READ,
70
            [
71 12
                'line-length' => $this->lineLength,
72 12
                'line-break-chars' => $this->lineBreak
73
            ]
74
        );
75 12
    }
76
77
    /**
78
     *
79
     */
80 1
    private function removeFilter(): void
81
    {
82 1
        if ($this->filter !== null) {
83 1
            stream_filter_remove($this->filter);
84
        }
85 1
    }
86
87
    /**
88
     * @return string
89
     */
90 4
    public function __toString(): string
91
    {
92 4
        $this->rewind();
93
        return $this->decoratedStream->__toString();
94
    }
95
96
    /**
97
     *
98
     */
99
    public function close(): void
100
    {
101
        $this->decoratedStream->close();
102
    }
103
104
    /**
105
     * @return mixed
106
     */
107
    public function detach()
108
    {
109
        return $this->decoratedStream->detach();
110
    }
111
112
    /**
113
     * @return int|null
114 1
     */
115
    public function getSize(): ?int
116 1
    {
117
        return null;
118
    }
119
120
    /**
121
     * @return int
122
     * @throws \RuntimeException
123 1
     */
124
    public function tell(): int
125 1
    {
126
        return $this->decoratedStream->tell();
127
    }
128
129
    /**
130
     * @return bool
131 3
     */
132
    public function eof(): bool
133 3
    {
134
        return $this->decoratedStream->eof();
135
    }
136
137
    /**
138
     * @return bool
139
     */
140
    public function isSeekable(): bool
141
    {
142
        return false;
143
    }
144
145
    /**
146
     * @param int $offset
147
     * @param int $whence
148
     * @return int
149 1
     */
150
    public function seek(int $offset, int $whence = SEEK_SET): int
151 1
    {
152
        return -1;
153
    }
154
155
    /**
156
     * @return bool
157 1
     */
158
    public function rewind(): bool
159 1
    {
160 1
        $this->removeFilter();
161
        if (!$this->decoratedStream->rewind()) {
162
            return false;
163
        }
164 1
165 1
        $this->applyFilter();
166
        return true;
167
    }
168
169
    /**
170
     * @return bool
171 1
     */
172
    public function isWritable(): bool
173 1
    {
174
        return false;
175
    }
176
177
    /**
178
     * @param $string
179
     * @return int
180 1
     */
181
    public function write($string): int
182 1
    {
183
        throw new \RuntimeException('Cannot write to stream');
184
    }
185
186
    /**
187
     * @return bool
188
     */
189
    public function isReadable(): bool
190
    {
191
        return true;
192
    }
193
194
    /**
195
     * @param int $length
196
     * @return string
197 4
     */
198
    public function read(int $length): string
199 4
    {
200
        return $this->decoratedStream->read($length);
201
    }
202
203
    /**
204
     * @return string
205 2
     */
206
    public function getContents(): string
207 2
    {
208
        return $this->decoratedStream->getContents();
209
    }
210
211
    /**
212
     * @param array $keys
213
     * @return array
214 2
     */
215
    public function getMetadata(array $keys = []): array
216 2
    {
217
        return $this->decoratedStream->getMetadata();
218
    }
219
}