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

OptimalTransferEncodedTextStream::getMetadata()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 15
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 15
ccs 10
cts 10
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 9
nc 1
nop 1
crap 1
1
<?php
2
declare(strict_types=1);
3
4
namespace Genkgo\Mail\Stream;
5
6
use Genkgo\Mail\StreamInterface;
7
8
final class OptimalTransferEncodedTextStream implements StreamInterface
9
{
10
    /**
11
     * @var StreamInterface
12
     */
13
    private $decoratedStream;
14
    /**
15
     * @var string
16
     */
17
    private $encoding = '7bit';
18
    /**
19
     * @var int
20
     */
21
    private $lineLength = 78;
22
    /**
23
     *
24
     */
25
    private CONST NON_7BIT_CHARS = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F\x7F\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xFF";
26
    /**
27
     * @var string
28
     */
29
    private $lineBreak;
30
31
    /**
32
     * OptimalEncodedTextStream constructor.
33
     * @param string $text
34
     * @param int $lineLength
35
     * @param string $lineBreak
36
     */
37 95
    public function __construct(string $text, int $lineLength = 78, string $lineBreak = "\r\n")
38
    {
39 95
        $this->lineLength = $lineLength;
40 95
        $this->lineBreak = $lineBreak;
41 95
        $this->decoratedStream = $this->calculateOptimalStream($text);
42 95
    }
43
44
    /**
45
     * @param string $text
46
     * @return StreamInterface
47
     */
48 95
    private function calculateOptimalStream(string $text): StreamInterface
49
    {
50 95
        if (strcspn($text, self::NON_7BIT_CHARS) === strlen($text)) {
51 86
            $this->encoding = '7bit';
52 86
            return new AsciiEncodedStream($text, $this->lineLength, $this->lineBreak);
53
        }
54
55 11
        if (preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $text) > (strlen($text) / 3)) {
56 9
            $this->encoding = 'base64';
57 9
            return Base64EncodedStream::fromString($text, $this->lineLength, $this->lineBreak);
58
        }
59
60 2
        $this->encoding = 'quoted-printable';
61 2
        return QuotedPrintableStream::fromString($text, $this->lineLength, $this->lineBreak);
62
    }
63
64
    /**
65
     * @return string
66
     */
67 77
    public function __toString(): string
68
    {
69 77
        return $this->decoratedStream->__toString();
70
    }
71
72
    /**
73
     *
74
     */
75
    public function close(): void
76
    {
77
        $this->decoratedStream->close();
78
    }
79
80
    /**
81
     * @return mixed
82
     */
83
    public function detach()
84
    {
85
        return $this->decoratedStream->detach();
86
    }
87
88
    /**
89
     * @return int|null
90
     */
91 4
    public function getSize(): ?int
92
    {
93 4
        return $this->decoratedStream->getSize();
94
    }
95
96
    /**
97
     * @return int
98
     * @throws \RuntimeException
99
     */
100
    public function tell(): int
101
    {
102
        return $this->decoratedStream->tell();
103
    }
104
105
    /**
106
     * @return bool
107
     */
108
    public function eof(): bool
109
    {
110
        return $this->decoratedStream->eof();
111
    }
112
113
    /**
114
     * @return bool
115
     */
116
    public function isSeekable(): bool
117
    {
118
        return $this->decoratedStream->isSeekable();
119
    }
120
121
    /**
122
     * @param int $offset
123
     * @param int $whence
124
     * @return int
125
     */
126
    public function seek(int $offset, int $whence = SEEK_SET): int
127
    {
128
        return $this->decoratedStream->seek($offset, $whence);
129
    }
130
131
    /**
132
     * @return bool
133
     */
134
    public function rewind(): bool
135
    {
136
        return $this->decoratedStream->rewind();
137
    }
138
139
    /**
140
     * @return bool
141
     */
142
    public function isWritable(): bool
143
    {
144
        return $this->decoratedStream->isWritable();
145
    }
146
147
    /**
148
     * @param $string
149
     * @return int
150
     */
151
    public function write($string): int
152
    {
153
        return $this->decoratedStream->write($string);
154
    }
155
156
    /**
157
     * @return bool
158
     */
159
    public function isReadable(): bool
160
    {
161
        return $this->decoratedStream->isReadable();
162
    }
163
164
    /**
165
     * @param int $length
166
     * @return string
167
     */
168
    public function read(int $length): string
169
    {
170
        return $this->decoratedStream->read($length);
171
    }
172
173
    /**
174
     * @return string
175
     */
176
    public function getContents(): string
177
    {
178
        return $this->decoratedStream->getContents();
179
    }
180
181
    /**
182
     * @param array $keys
183
     * @return array
184
     */
185 95
    public function getMetadata(array $keys = []): array
186
    {
187 95
        $metaData = $this->decoratedStream->getMetadata($keys);
188 95
        $metaData['transfer-encoding'] = $this->encoding;
189
190 95
        $keys = array_map('strtolower', $keys);
191
192 95
        return array_filter(
193 95
            $metaData,
194 95
            function ($key) use ($keys) {
195 95
                return in_array(strtolower($key), $keys);
196 95
            },
197 95
            ARRAY_FILTER_USE_KEY
198
        );
199
    }
200
201
}