Completed
Push — id3-metadata-objects ( a3a88a...ecc500 )
by Daniel
03:22
created

FrameReader::getInteger24Reader()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 9
Ratio 100 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 9
loc 9
rs 9.6666
cc 2
eloc 5
nc 2
nop 0
1
<?php
2
/**
3
 * This file is part of the Metadata package.
4
 *
5
 * @author Daniel Schröder <[email protected]>
6
 */
7
8
namespace GravityMedia\Metadata\ID3v2\Reader;
9
10
use GravityMedia\Metadata\ID3v2\Encoder\Unsynchronisation;
11
use GravityMedia\Metadata\ID3v2\Enum\FrameFlag;
12
use GravityMedia\Metadata\ID3v2\Enum\Version;
13
use GravityMedia\Metadata\ID3v2\Frame;
14
use GravityMedia\Metadata\ID3v2\Stream\SynchsafeInteger32Reader;
15
use GravityMedia\Metadata\ID3v2\FrameInterface;
16
use GravityMedia\Metadata\ID3v2\HeaderInterface;
17
use GravityMedia\Stream\Enum\ByteOrder;
18
use GravityMedia\Stream\Reader\Integer16Reader;
19
use GravityMedia\Stream\Reader\Integer24Reader;
20
use GravityMedia\Stream\Reader\Integer32Reader;
21
use GravityMedia\Stream\StreamInterface;
22
23
/**
24
 * ID3v2 Frame reader
25
 *
26
 * @package GravityMedia\Metadata\ID3v2\Reader
27
 */
28
class FrameReader
29
{
30
    /**
31
     * @var StreamInterface
32
     */
33
    protected $stream;
34
35
    /**
36
     * @var HeaderInterface
37
     */
38
    protected $header;
39
40
    /**
41
     * @var Integer16Reader
42
     */
43
    protected $integer16Reader;
44
45
    /**
46
     * @var Integer24Reader
47
     */
48
    protected $integer24Reader;
49
50
    /**
51
     * @var Integer32Reader
52
     */
53
    protected $integer32Reader;
54
55
    /**
56
     * @var SynchsafeInteger32Reader
57
     */
58
    protected $synchsafeInteger32Reader;
59
60
    /**
61
     * Create ID3v2 frame reader.
62
     *
63
     * @param StreamInterface $stream
64
     * @param HeaderInterface $header
65
     */
66
    public function __construct(StreamInterface $stream, HeaderInterface $header)
67
    {
68
        $this->stream = $stream;
69
        $this->header = $header;
70
    }
71
72
    /**
73
     * Get 16-bit integer reader
74
     *
75
     * @return Integer16Reader
76
     */
77 View Code Duplication
    public function getInteger16Reader()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
78
    {
79
        if (null === $this->integer16Reader) {
80
            $this->integer16Reader = new Integer16Reader($this->stream);
81
            $this->integer16Reader->setByteOrder(ByteOrder::BIG_ENDIAN);
82
        }
83
84
        return $this->integer16Reader;
85
    }
86
87
    /**
88
     * Get 24-bit integer reader
89
     *
90
     * @return Integer24Reader
91
     */
92 View Code Duplication
    public function getInteger24Reader()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
93
    {
94
        if (null === $this->integer24Reader) {
95
            $this->integer24Reader = new Integer24Reader($this->stream);
96
            $this->integer24Reader->setByteOrder(ByteOrder::BIG_ENDIAN);
97
        }
98
99
        return $this->integer24Reader;
100
    }
101
102
    /**
103
     * Get 32-bit integer reader
104
     *
105
     * @return Integer32Reader
106
     */
107 View Code Duplication
    public function getInteger32Reader()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
108
    {
109
        if (null === $this->integer32Reader) {
110
            $this->integer32Reader = new Integer32Reader($this->stream);
111
            $this->integer32Reader->setByteOrder(ByteOrder::BIG_ENDIAN);
112
        }
113
114
        return $this->integer32Reader;
115
    }
116
117
    /**
118
     * Get synchsafe 32-bit integer reader
119
     *
120
     * @return SynchsafeInteger32Reader
121
     */
122 View Code Duplication
    public function getSynchsafeInteger32Reader()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
123
    {
124
        if (null === $this->synchsafeInteger32Reader) {
125
            $this->synchsafeInteger32Reader = new SynchsafeInteger32Reader($this->stream);
126
            $this->synchsafeInteger32Reader->setByteOrder(ByteOrder::BIG_ENDIAN);
127
        }
128
129
        return $this->synchsafeInteger32Reader;
130
    }
131
132
    /**
133
     * Read ID3v2 frame name.
134
     *
135
     * @return string
136
     */
137
    protected function readName()
138
    {
139
        if (Version::VERSION_22 === $this->header->getVersion()) {
140
            return rtrim($this->stream->read(3));
141
        }
142
143
        return rtrim($this->stream->read(4));
144
    }
145
146
    /**
147
     * Read ID3v2 frame size.
148
     *
149
     * @return int
150
     */
151
    protected function readSize()
152
    {
153
        if (Version::VERSION_22 === $this->header->getVersion()) {
154
            return $this->getInteger24Reader()->read();
155
        }
156
157
        if (Version::VERSION_23 === $this->header->getVersion()) {
158
            return $this->getInteger32Reader()->read();
159
        }
160
161
        return $this->getSynchsafeInteger32Reader()->read();
162
    }
163
164
    /**
165
     * Read ID3v2 frame flags.
166
     *
167
     * @return array
168
     */
169
    protected function readFlags()
170
    {
171
        $flags = $this->getInteger16Reader()->read();
172
173
        if (Version::VERSION_23 === $this->header->getVersion()) {
174
            return [
175
                FrameFlag::FLAG_TAG_ALTER_PRESERVATION => (bool)($flags & 0x8000),
176
                FrameFlag::FLAG_FILE_ALTER_PRESERVATION => (bool)($flags & 0x4000),
177
                FrameFlag::FLAG_READ_ONLY => (bool)($flags & 0x2000),
178
                FrameFlag::FLAG_COMPRESSION => (bool)($flags & 0x0080),
179
                FrameFlag::FLAG_ENCRYPTION => (bool)($flags & 0x0040),
180
                FrameFlag::FLAG_GROUPING_IDENTITY => (bool)($flags & 0x0020),
181
            ];
182
        }
183
184
        return [
185
            FrameFlag::FLAG_TAG_ALTER_PRESERVATION => (bool)($flags & 0x4000),
186
            FrameFlag::FLAG_FILE_ALTER_PRESERVATION => (bool)($flags & 0x2000),
187
            FrameFlag::FLAG_READ_ONLY => (bool)($flags & 0x1000),
188
            FrameFlag::FLAG_GROUPING_IDENTITY => (bool)($flags & 0x0040),
189
            FrameFlag::FLAG_COMPRESSION => (bool)($flags & 0x0008),
190
            FrameFlag::FLAG_ENCRYPTION => (bool)($flags & 0x0004),
191
            FrameFlag::FLAG_UNSYNCHRONISATION => (bool)($flags & 0x0002),
192
            FrameFlag::FLAG_DATA_LENGT_INDICATOR => (bool)($flags & 0x0001),
193
        ];
194
    }
195
196
    /**
197
     * Read ID3v2 frame data length.
198
     *
199
     * @return int
200
     */
201
    public function readDataLength()
202
    {
203
        return $this->getSynchsafeInteger32Reader()->read();
204
    }
205
206
    /**
207
     * Read ID3v2 frame.
208
     *
209
     * @return FrameInterface
210
     */
211
    public function read()
212
    {
213
        $name = $this->readName();
214
        $size = $this->readSize();
215
216
        $frame = new Frame();
217
        $frame
218
            ->setName($name)
219
            ->setSize($size);
220
221
        // Return empty frame
222
        if (0 === $size) {
223
            return $frame;
224
        }
225
226
        // Only in ID3v2.3 and ID3v2.4
227
        if (Version::VERSION_22 !== $this->header->getVersion()) {
228
            $frame->setFlags($this->readFlags());
229
        }
230
231
        // Only in ID3v2.4
232
        if ($frame->isFlagEnabled(FrameFlag::FLAG_DATA_LENGT_INDICATOR)) {
233
            $frame->setDataLength($this->readDataLength());
234
235
            $size -= 4;
236
        }
237
238
239
        $data = $this->stream->read($size);
240
        if ($frame->isFlagEnabled(FrameFlag::FLAG_COMPRESSION)) {
241
            $data = gzuncompress($data);
242
        }
243
244
        // Only in ID3v2.4
245
        if ($frame->isFlagEnabled(FrameFlag::FLAG_UNSYNCHRONISATION)) {
246
            $data = Unsynchronisation::decode($data);
247
        }
248
249
        return $frame
250
            ->setData($data);
251
    }
252
}
253