Completed
Push — id3-metadata-objects ( 384c4c...c855dc )
by Daniel
02:54
created

Metadata::readData()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 15
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 15
ccs 0
cts 10
cp 0
rs 9.4285
cc 3
eloc 8
nc 4
nop 1
crap 12
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;
9
10
use GravityMedia\Metadata\Exception\InvalidArgumentException;
11
use GravityMedia\Metadata\Exception\RuntimeException;
12
use GravityMedia\Metadata\ID3v2\Filter\CompressionFilter;
13
use GravityMedia\Metadata\ID3v2\Filter\UnsynchronisationFilter;
14
use GravityMedia\Metadata\ID3v2\Flag\ExtendedHeaderFlag;
15
use GravityMedia\Metadata\ID3v2\Flag\FrameFlag;
16
use GravityMedia\Metadata\ID3v2\Flag\HeaderFlag;
17
use GravityMedia\Metadata\ID3v2\Metadata\ExtendedHeaderMetadata;
18
use GravityMedia\Metadata\ID3v2\Metadata\Frame\TextInformationFrame;
19
use GravityMedia\Metadata\ID3v2\Metadata\FrameMetadata;
20
use GravityMedia\Metadata\ID3v2\Metadata\HeaderMetadata;
21
use GravityMedia\Stream\ByteOrder;
22
use GravityMedia\Stream\Stream;
23
24
/**
25
 * ID3v2 metadata class.
26
 *
27
 * @package GravityMedia\Metadata\ID3v2
28
 */
29
class Metadata
30
{
31
    /**
32
     * @var Stream
33
     */
34
    protected $stream;
35
36
    /**
37
     * @var CompressionFilter
38
     */
39
    protected $compressionFilter;
40
41
    /**
42
     * @var UnsynchronisationFilter
43
     */
44
    protected $unsynchronisationFilter;
45
46
    /**
47
     * Create ID3v2 metadata object from resource.
48
     *
49
     * @param resource $resource
50
     *
51
     * @throws InvalidArgumentException An exception will be thrown for invalid resource arguments.
52
     *
53
     * @return static
54
     */
55
    public static function fromResource($resource)
56
    {
57
        if (!is_resource($resource)) {
58
            throw new InvalidArgumentException('Invalid resource');
59
        }
60
61
        $stream = Stream::fromResource($resource);
62
        $stream->setByteOrder(ByteOrder::BIG_ENDIAN);
63
64
        $metadata = new static();
65
        $metadata->stream = $stream;
66
        $metadata->compressionFilter = new CompressionFilter();
67
        $metadata->unsynchronisationFilter = new UnsynchronisationFilter();
68
69
        return $metadata;
70
    }
71
72
    /**
73
     * Returns whether ID3v2 metadata exists.
74
     *
75
     * @return bool
76
     */
77 View Code Duplication
    public function exists()
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 ($this->stream->getSize() < 10) {
80
            return false;
81
        }
82
83
        $this->stream->seek(0);
84
85
        return 'ID3' === $this->stream->read(3);
86
    }
87
88
    /**
89
     * Strip ID3v2 metadata.
90
     *
91
     * @return $this
92
     */
93
    public function strip()
94
    {
95
        // TODO: implement
96
97
        return $this;
98
    }
99
100
    /**
101
     * Read ID3v2 version.
102
     *
103
     * @throws RuntimeException An exception is thrown on invalid versions.
104
     *
105
     * @return int
106
     */
107
    public function readVersion()
108
    {
109
        $this->stream->seek(3);
110
111
        switch ($this->stream->readUInt8()) {
112
            case 2:
113
                return Version::VERSION_22;
114
            case 3:
115
                return Version::VERSION_23;
116
            case 4:
117
                return Version::VERSION_24;
118
        }
119
120
        throw new RuntimeException('Invalid version.');
121
    }
122
123
    /**
124
     * Read ID3v2 revision.
125
     *
126
     * @return int
127
     */
128
    public function readRevision()
129
    {
130
        $this->stream->seek(4);
131
132
        return $this->stream->readUInt8();
133
    }
134
135
    /**
136
     * Read ID3v2 tag.
137
     *
138
     * @return null|Tag
139
     */
140
    public function read()
141
    {
142
        if (!$this->exists()) {
143
            return null;
144
        }
145
146
        $version = $this->readVersion();
147
        $revision = $this->readRevision();
148
        $tag = new Tag($version, $revision);
149
150
        $headerMetadata = new HeaderMetadata($this->stream, $version);
151
        $header = new Header();
152
        $header->setSize($headerMetadata->readSize());
153
        $header->setFlags($headerMetadata->readFlags());
154
155
        $this->stream->seek(10);
156
        $data = $this->stream->read($header->getSize());
157
158
        if ($header->isFlagEnabled(HeaderFlag::FLAG_COMPRESSION)) {
159
            $data = $this->compressionFilter->decode($data);
160
        }
161
162
        if ($header->isFlagEnabled(HeaderFlag::FLAG_UNSYNCHRONISATION)) {
163
            $data = $this->unsynchronisationFilter->decode($data);
164
        }
165
166
        $tagStream = Stream::fromResource(fopen('php://temp', 'r+'));
167
        $tagStream->setByteOrder(ByteOrder::BIG_ENDIAN);
168
        $tagStream->write($data);
169
        $tagStream->rewind();
170
171
        $tagSize = $tagStream->getSize();
172
173
        if ($header->isFlagEnabled(HeaderFlag::FLAG_EXTENDED_HEADER)) {
174
            $extendedHeaderMetadata = new ExtendedHeaderMetadata($tagStream, $version);
175
            $extendedHeader = new ExtendedHeader();
176
            $extendedHeader->setSize($extendedHeaderMetadata->readSize());
177
            $extendedHeader->setFlags($extendedHeaderMetadata->readFlags());
178
179
            if (Version::VERSION_23 === $version) {
180
                $tag->setPadding($extendedHeaderMetadata->readPadding());
181
            }
182
183
            if ($extendedHeader->isFlagEnabled(ExtendedHeaderFlag::FLAG_TAG_IS_AN_UPDATE)) {
184
                $tagStream->seek(1, SEEK_CUR);
185
            }
186
187
            if ($extendedHeader->isFlagEnabled(ExtendedHeaderFlag::FLAG_CRC_DATA_PRESENT)) {
188
                $tag->setCrc32($extendedHeaderMetadata->readCrc32());
189
            }
190
191
            if ($extendedHeader->isFlagEnabled(ExtendedHeaderFlag::FLAG_TAG_RESTRICTIONS)) {
192
                $tag->setRestrictions($extendedHeaderMetadata->readRestrictions());
193
            }
194
195
            $tagSize -= $extendedHeader->getSize();
196
        }
197
198
        if ($header->isFlagEnabled(HeaderFlag::FLAG_FOOTER_PRESENT)) {
199
            // TODO: Read footer metadata
200
201
            $tagSize -= 10;
202
        }
203
204
        $frameMetadata = new FrameMetadata($tagStream, $version);
205
206
        while ($tagSize > 0) {
207
            $frameName = $frameMetadata->readName();
208
            $frameSize = $frameMetadata->readSize();
209
210
            if (0 === $frameSize) {
211
                break;
212
            }
213
214
            $frame = new Frame();
215
            $frame->setName($frameName);
216
            $frame->setSize($frameSize);
217
218
            if (Version::VERSION_22 !== $version) {
219
                $frame->setFlags($frameMetadata->readFlags());
220
            }
221
222
            if ($frame->isFlagEnabled(FrameFlag::FLAG_DATA_LENGT_INDICATOR)) {
223
                $frame->setDataLength($frameMetadata->readDataLength());
224
225
                $frameSize -= 4;
226
            }
227
228
            $data = $tagStream->read($frameSize);
229
230
            if ($frame->isFlagEnabled(FrameFlag::FLAG_COMPRESSION)) {
231
                $data = $this->compressionFilter->decode($data);
232
            }
233
234
            if ($frame->isFlagEnabled(FrameFlag::FLAG_UNSYNCHRONISATION)) {
235
                $data = $this->unsynchronisationFilter->decode($data);
236
            }
237
238
            $frame->setData($data);
239
240
            $tag->addFrame($frame);
241
242
            $frameStream = Stream::fromResource(fopen('php://temp', 'r+'));
243
            $frameStream->setByteOrder(ByteOrder::BIG_ENDIAN);
244
            $frameStream->write($data);
245
            $frameStream->rewind();
246
247
            if ('T' === substr($frameName, 0, 1)) {
248
                $textInformationFrame = new TextInformationFrame($frameStream, $version);
249
                $textEncoding = $textInformationFrame->readTextEncoding();
0 ignored issues
show
Unused Code introduced by
$textEncoding is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
250
251
                //var_dump($textEncoding);
252
                //var_dump($textInformationFrame->readInformation($textEncoding));
0 ignored issues
show
Unused Code Comprehensibility introduced by
80% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
253
            }
254
255
            $tagSize -= $frame->getSize();
256
        }
257
258
        return $tag;
259
    }
260
261
    /**
262
     * Write ID3v2 tag.
263
     *
264
     * @param Tag $tag The tag to write.
265
     *
266
     * @return $this
267
     */
268
    public function write(Tag $tag)
0 ignored issues
show
Unused Code introduced by
The parameter $tag is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
269
    {
270
        // TODO: implement
271
272
        return $this;
273
    }
274
}
275