Passed
Branch master (f2d2e3)
by Michael
18:45
created

getid3   F

Complexity

Total Complexity 140

Size/Duplication

Total Lines 1115
Duplicated Lines 0.81 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 0
Metric Value
dl 9
loc 1115
rs 1.1399
c 0
b 0
f 0
wmc 140
lcom 1
cbo 4

11 Methods

Rating   Name   Duplication   Size   Complexity  
B GetFileFormatArray() 0 474 1
B iconv() 0 23 7
A warning() 0 7 2
D HandleAllTags() 0 99 23
A include_module_optional() 0 8 2
B __construct() 0 60 10
F Analyze() 0 310 86
A __clone() 0 8 1
A include_module() 0 7 2
A warnings() 0 3 1
A CharConvert() 0 18 5

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like getid3 often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use getid3, and based on these observations, apply Extract Interface, too.

1
<?php
2
// +----------------------------------------------------------------------+
3
// | PHP version 5                                                        |
4
// +----------------------------------------------------------------------+
5
// | Copyright (c) 2002-2006 James Heinrich, Allan Hansen                 |
6
// +----------------------------------------------------------------------+
7
// | This source file is subject to version 2 of the GPL license,         |
8
// | that is bundled with this package in the file license.txt and is     |
9
// | available through the world-wide-web at the following url:           |
10
// | http://www.gnu.org/copyleft/gpl.html                                 |
11
// +----------------------------------------------------------------------+
12
// | getID3() - http://getid3.sourceforge.net or http://www.getid3.org    |
13
// +----------------------------------------------------------------------+
14
// | Authors: James Heinrich <info�getid3*org>                            |
15
// |          Allan Hansen <ah�artemis*dk>                                |
16
// +----------------------------------------------------------------------+
17
// | getid3.php                                                           |
18
// | Main getID3() file.                                                  |
19
// | dependencies: modules.                                               |
20
// +----------------------------------------------------------------------+
21
//
22
// $Id: getid3.php,v 1.26 2006/12/25 23:44:23 ah Exp $
23
24
25
class getid3
26
{
27
    //// Settings Section - do NOT modify this file - change setting after newing getid3!
28
29
    // Encoding
30
    public $encoding                 = 'ISO-8859-1';      // CASE SENSITIVE! - i.e. (must be supported by iconv() - see http://www.gnu.org/software/libiconv/).  Examples:  ISO-8859-1  UTF-8  UTF-16  UTF-16BE.
31
    public $encoding_id3v1           = 'ISO-8859-1';      // Override SPECIFICATION encoding for broken ID3v1 tags caused by bad tag programs. Examples: 'EUC-CN' for "Chinese MP3s" and 'CP1251' for "Cyrillic".
32
    public $encoding_id3v2           = 'ISO-8859-1';      // Override ISO-8859-1 encoding for broken ID3v2 tags caused by BRAINDEAD tag programs that writes system codepage as 'ISO-8859-1' instead of UTF-8.
33
34
    // Tags - disable for speed
35
    public $option_tag_id3v1         = true;              // Read and process ID3v1 tags.
36
    public $option_tag_id3v2         = true;              // Read and process ID3v2 tags.
37
    public $option_tag_lyrics3       = true;              // Read and process Lyrics3 tags.
38
    public $option_tag_apetag        = true;              // Read and process APE tags.
39
40
    // Misc calucations - disable for speed
41
    public $option_analyze           = true;              // Analyze file - disable if you only need to detect file format.
42
    public $option_accurate_results  = true;              // Disable to greatly speed up parsing of some file formats at the cost of accuracy.
43
    public $option_tags_process      = true;              // Copy tags to root key 'tags' and 'comments' and encode to $this->encoding.
44
    public $option_tags_images       = false;             // Scan tags for binary image data - ID3v2 and vorbiscomments only.
45
    public $option_extra_info        = true;              // Calculate/return additional info such as bitrate, channelmode etc.
46
    public $option_max_2gb_check     = false;             // Check whether file is larger than 2 Gb and thus not supported by PHP.
47
48
    // Misc data hashes - slow - require hash module
49
    public $option_md5_data          = false;             // Get MD5 sum of data part - slow.
50
    public $option_md5_data_source   = false;             // Use MD5 of source file if available - only FLAC, MAC, OptimFROG and Wavpack4.
51
    public $option_sha1_data         = false;             // Get SHA1 sum of data part - slow.
52
53
    // Public variables
54
    public $filename;                                     // Filename of file being analysed.
55
    public $fp;                                           // Filepointer to file being analysed.
56
    public $info;                                         // Result array.
57
58
    // Protected variables
59
    protected $include_path;                              // getid3 include path.
60
    protected $warnings = array ();
61
    protected $iconv_present;
62
63
    // Class constants
64
    const VERSION           = '2.0.0b4';
65
    const FREAD_BUFFER_SIZE = 16384;                      // Read buffer size in bytes.
66
    const ICONV_TEST_STRING = ' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~�������������������������������� ��������������������������������������������������������������������������������������������';
67
68
69
70
    // Constructor - check PHP enviroment and load library.
71
    public function __construct() {
72
73
        // Static varibles - no need to recalc every time we new getid3.
74
        static $include_path;
75
        static $iconv_present;
76
77
78
        static $initialized;
79
        if ($initialized) {
80
81
            // Import static variables
82
            $this->include_path  = $include_path;
83
            $this->iconv_present = $iconv_present;
84
85
            // Run init checks only on first instance.
86
            return;
87
        }
88
89
        // Get include_path
90
        $this->include_path = $include_path = dirname(__FILE__) . '/';
91
92
        // Check for presence of iconv() and make sure it works (simpel test only).
93
        if (function_exists('iconv') && @iconv('UTF-16LE', 'ISO-8859-1', @iconv('ISO-8859-1', 'UTF-16LE', getid3::ICONV_TEST_STRING)) == getid3::ICONV_TEST_STRING) {
94
            $this->iconv_present = $iconv_present = true;
95
        }
96
97
        // iconv() not present - load replacement module.
98
        else {
99
            $this->include_module('lib.iconv_replacement');
100
            $this->iconv_present = $iconv_present = false;
101
        }
102
103
104
        // Require magic_quotes_runtime off
105
        if (get_magic_quotes_runtime()) {
106
            throw new getid3_exception('magic_quotes_runtime must be disabled before running getID3(). Surround getid3 block by set_magic_quotes_runtime(0) and set_magic_quotes_runtime(1).');
107
        }
108
109
110
        // Check memory limit.
111
        $memory_limit = ini_get('memory_limit');
112
        if (eregi('([0-9]+)M', $memory_limit, $matches)) {
0 ignored issues
show
Deprecated Code introduced by
The function eregi() has been deprecated: 5.3.0 Use preg_match() instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

112
        if (/** @scrutinizer ignore-deprecated */ eregi('([0-9]+)M', $memory_limit, $matches)) {

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
113
            // could be stored as "16M" rather than 16777216 for example
114
            $memory_limit = $matches[1] * 1048576;
115
        }
116
        if ($memory_limit <= 0) {
117
            // Should not happen.
118
        } elseif ($memory_limit <= 4194304) {
119
            $this->warning('[SERIOUS] PHP has less than 4 Mb available memory and will very likely run out. Increase memory_limit in php.ini.');
120
        } elseif ($memory_limit <= 12582912) {
121
            $this->warning('PHP has less than 12 Mb available memory and might run out if all modules are loaded. Increase memory_limit in php.ini if needed.');
122
        }
123
124
125
        // Check safe_mode off
126
        if ((bool)ini_get('safe_mode')) {
127
            $this->warning('Safe mode is on, shorten support disabled, md5data/sha1data for ogg vorbis disabled, ogg vorbis/flac tag writing disabled.');
128
        }
129
130
        $initialized = true;
131
    }
132
133
134
135
    // Analyze file by name
136
    public function Analyze($filename) {
137
138
        // Init and save values
139
        $this->filename = $filename;
140
        $this->warnings = array ();
141
142
        // Init result array and set parameters
143
        $this->info = array ();
144
        $this->info['GETID3_VERSION'] = getid3::VERSION;
145
146
        // Remote files not supported
147
        if (preg_match('/^(ht|f)tp:\/\//', $filename)) {
148
            throw new getid3_exception('Remote files are not supported - please copy the file locally first.');
149
        }
150
151
        // Open local file
152
        if (!$this->fp = @fopen($filename, 'rb')) {
153
            throw new getid3_exception('Could not open file "'.$filename.'"');
154
        }
155
156
        // Set filesize related parameters
157
        $this->info['filesize']     = filesize($filename);
158
        $this->info['avdataoffset'] = 0;
159
        $this->info['avdataend']    = $this->info['filesize'];
160
161
        // Option_max_2gb_check
162
        if ($this->option_max_2gb_check) {
163
            // PHP doesn't support integers larger than 31-bit (~2GB)
164
            // filesize() simply returns (filesize % (pow(2, 32)), no matter the actual filesize
165
            // ftell() returns 0 if seeking to the end is beyond the range of unsigned integer
166
            fseek($this->fp, 0, SEEK_END);
167
            if ((($this->info['filesize'] != 0) && (ftell($this->fp) == 0)) ||
168
                ($this->info['filesize'] < 0) ||
169
                (ftell($this->fp) < 0)) {
170
                    unset($this->info['filesize']);
171
                    fclose($this->fp);
172
                    throw new getid3_exception('File is most likely larger than 2GB and is not supported by PHP.');
173
            }
174
        }
175
176
177
        // ID3v2 detection (NOT parsing) done to make fileformat easier.
178
        if (!$this->option_tag_id3v2) {
179
180
            fseek($this->fp, 0, SEEK_SET);
181
            $header = fread($this->fp, 10);
182
            if (substr($header, 0, 3) == 'ID3'  &&  strlen($header) == 10) {
183
                $this->info['id3v2']['header']        = true;
184
                $this->info['id3v2']['majorversion']  = ord($header{3});
185
                $this->info['id3v2']['minorversion']  = ord($header{4});
186
                $this->info['avdataoffset']          += getid3_lib::BigEndian2Int(substr($header, 6, 4), 1) + 10; // length of ID3v2 tag in 10-byte header doesn't include 10-byte header length
187
            }
188
        }
189
190
191
        // Handle tags
192
        foreach (array ("id3v2", "id3v1", "apetag", "lyrics3") as $tag_name) {
193
194
            $option_tag = 'option_tag_' . $tag_name;
195
            if ($this->$option_tag) {
196
                $this->include_module('tag.'.$tag_name);
197
                try {
198
                    $tag_class = 'getid3_' . $tag_name;
199
                    $tag = new $tag_class($this);
200
                    $tag->Analyze();
201
                }
202
                catch (getid3_exception $e) {
203
                    throw $e;
204
                }
205
            }
206
        }
207
208
209
210
        //// Determine file format by magic bytes in file header.
211
212
        // Read 32 kb file data
213
        fseek($this->fp, $this->info['avdataoffset'], SEEK_SET);
214
        $filedata = fread($this->fp, 32774);
215
216
        // Get huge FileFormatArray
217
        $file_format_array = getid3::GetFileFormatArray();
218
219
        // Identify file format - loop through $format_info and detect with reg expr
220
        foreach ($file_format_array as $name => $info) {
221
222
            if (preg_match('/'.$info['pattern'].'/s', $filedata)) {                         // The /s switch on preg_match() forces preg_match() NOT to treat newline (0x0A) characters as special chars but do a binary match
223
224
                // Format detected but not supported
225
                if (!@$info['module'] || !@$info['group']) {
226
                    fclose($this->fp);
227
                    $this->info['fileformat'] = $name;
228
                    $this->info['mime_type']  = $info['mime_type'];
229
                    $this->warning('Format only detected. Parsing not available yet.');
230
                    $this->info['warning'] = $this->warnings;
231
                    return $this->info;
232
                }
233
234
                $determined_format = $info;  // copy $info deleted by foreach()
235
                continue;
236
            }
237
        }
238
239
        // Unable to determine file format
240
        if (!@$determined_format) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $determined_format does not seem to be defined for all execution paths leading up to this point.
Loading history...
241
242
            // Too many mp3 encoders on the market put gabage in front of mpeg files
243
            // use assume format on these if format detection failed
244
            if (preg_match('/\.mp[123a]$/i', $filename)) {
245
                $determined_format = $file_format_array['mp3'];
246
            }
247
248
            else {
249
                fclose($this->fp);
250
                throw new getid3_exception('Unable to determine file format');
251
            }
252
        }
253
254
        // Free memory
255
        unset($file_format_array);
256
257
        // Check for illegal ID3 tags
258
        if (@$determined_format['fail_id3'] && (@$this->info['id3v1'] || @$this->info['id3v2'])) {
259
            if ($determined_format['fail_id3'] === 'ERROR') {
260
                fclose($this->fp);
261
                throw new getid3_exception('ID3 tags not allowed on this file type.');
262
            }
263
            elseif ($determined_format['fail_id3'] === 'WARNING') {
264
                @$this->info['id3v1'] and $this->warning('ID3v1 tags not allowed on this file type.');
265
                @$this->info['id3v2'] and $this->warning('ID3v2 tags not allowed on this file type.');
266
            }
267
        }
268
269
        // Check for illegal APE tags
270
        if (@$determined_format['fail_ape'] && @$this->info['tags']['ape']) {
271
            if ($determined_format['fail_ape'] === 'ERROR') {
272
                fclose($this->fp);
273
                throw new getid3_exception('APE tags not allowed on this file type.');
274
            } elseif ($determined_format['fail_ape'] === 'WARNING') {
275
                $this->warning('APE tags not allowed on this file type.');
276
            }
277
        }
278
279
280
        // Set mime type
281
        $this->info['mime_type'] = $determined_format['mime_type'];
282
283
        // Calc module file name
284
        $determined_format['include'] = 'module.'.$determined_format['group'].'.'.$determined_format['module'].'.php';
285
286
        // Supported format signature pattern detected, but module deleted.
287
        if (!file_exists($this->include_path.$determined_format['include'])) {
288
            fclose($this->fp);
289
            throw new getid3_exception('Format not supported, module, '.$determined_format['include'].', was removed.');
290
        }
291
292
        // Include module
293
        $this->include_module($determined_format['group'].'.'.$determined_format['module']);
294
295
        // Instantiate module class and analyze
296
        $class_name = 'getid3_'.$determined_format['module'];
297
        if (!class_exists($class_name)) {
298
            throw new getid3_exception('Format not supported, module, '.$determined_format['include'].', is corrupt.');
299
        }
300
        $class = new $class_name($this);
301
302
        try {
303
             $this->option_analyze and $class->Analyze();
304
            }
305
        catch (getid3_exception $e) {
306
            throw $e;
307
        }
308
        catch (Exception $e) {
309
            throw new getid3_exception('Corrupt file.');
310
        }
311
312
        // Close file
313
        fclose($this->fp);
314
315
        // Optional - Process all tags - copy to 'tags' and convert charsets
316
        if ($this->option_tags_process) {
317
            $this->HandleAllTags();
318
        }
319
320
321
        //// Optional - perform more calculations
322
        if ($this->option_extra_info) {
323
324
            // Set channelmode on audio
325
            if (@$this->info['audio']['channels'] == '1') {
326
                $this->info['audio']['channelmode'] = 'mono';
327
            } elseif (@$this->info['audio']['channels'] == '2') {
328
                $this->info['audio']['channelmode'] = 'stereo';
329
            }
330
331
            // Calculate combined bitrate - audio + video
332
            $combined_bitrate  = 0;
333
            $combined_bitrate += (isset($this->info['audio']['bitrate']) ? $this->info['audio']['bitrate'] : 0);
334
            $combined_bitrate += (isset($this->info['video']['bitrate']) ? $this->info['video']['bitrate'] : 0);
335
            if (($combined_bitrate > 0) && empty($this->info['bitrate'])) {
336
                $this->info['bitrate'] = $combined_bitrate;
337
            }
338
            if (!isset($this->info['playtime_seconds']) && !empty($this->info['bitrate'])) {
339
                $this->info['playtime_seconds'] = (($this->info['avdataend'] - $this->info['avdataoffset']) * 8) / $this->info['bitrate'];
340
            }
341
342
            // Set playtime string
343
            if (!empty($this->info['playtime_seconds']) && empty($this->info['playtime_string'])) {
344
                $this->info['playtime_string'] =  floor(round($this->info['playtime_seconds']) / 60) . ':' . str_pad(floor(round($this->info['playtime_seconds']) % 60), 2, 0, STR_PAD_LEFT);;
345
            }
346
347
348
            // CalculateCompressionRatioVideo() {
349
            if (@$this->info['video'] && @$this->info['video']['resolution_x'] && @$this->info['video']['resolution_y'] && @$this->info['video']['bits_per_sample']) {
350
351
                // From static image formats
352
                if (in_array($this->info['video']['dataformat'], array ('bmp', 'gif', 'jpeg', 'jpg', 'png', 'tiff'))) {
353
                    $frame_rate         = 1;
354
                    $bitrate_compressed = $this->info['filesize'] * 8;
355
                }
356
357
                // From video formats
358
                else {
359
                    $frame_rate         = @$this->info['video']['frame_rate'];
360
                    $bitrate_compressed = @$this->info['video']['bitrate'];
361
                }
362
363
                if ($frame_rate && $bitrate_compressed) {
364
                    $this->info['video']['compression_ratio'] = $bitrate_compressed / ($this->info['video']['resolution_x'] * $this->info['video']['resolution_y'] * $this->info['video']['bits_per_sample'] * $frame_rate);
365
                }
366
            }
367
368
369
            // CalculateCompressionRatioAudio() {
370
            if (@$this->info['audio']['bitrate'] && @$this->info['audio']['channels'] && @$this->info['audio']['sample_rate']) {
371
                $this->info['audio']['compression_ratio'] = $this->info['audio']['bitrate'] / ($this->info['audio']['channels'] * $this->info['audio']['sample_rate'] * (@$this->info['audio']['bits_per_sample'] ? $this->info['audio']['bits_per_sample'] : 16));
372
            }
373
374
            if (@$this->info['audio']['streams']) {
375
                foreach ($this->info['audio']['streams'] as $stream_number => $stream_data) {
376
                    if (@$stream_data['bitrate'] && @$stream_data['channels'] && @$stream_data['sample_rate']) {
377
                        $this->info['audio']['streams'][$stream_number]['compression_ratio'] = $stream_data['bitrate'] / ($stream_data['channels'] * $stream_data['sample_rate'] * (@$stream_data['bits_per_sample'] ? $stream_data['bits_per_sample'] : 16));
378
                    }
379
                }
380
            }
381
382
383
            // CalculateReplayGain() {
384
            if (@$this->info['replay_gain']) {
385
                if (!@$this->info['replay_gain']['reference_volume']) {
386
                     $this->info['replay_gain']['reference_volume'] = 89;
387
                }
388
                if (isset($this->info['replay_gain']['track']['adjustment'])) {
389
                    $this->info['replay_gain']['track']['volume'] = $this->info['replay_gain']['reference_volume'] - $this->info['replay_gain']['track']['adjustment'];
390
                }
391
                if (isset($this->info['replay_gain']['album']['adjustment'])) {
392
                    $this->info['replay_gain']['album']['volume'] = $this->info['replay_gain']['reference_volume'] - $this->info['replay_gain']['album']['adjustment'];
393
                }
394
395
                if (isset($this->info['replay_gain']['track']['peak'])) {
396
                    $this->info['replay_gain']['track']['max_noclip_gain'] = 0 - 20 * log10($this->info['replay_gain']['track']['peak']);
397
                }
398
                if (isset($this->info['replay_gain']['album']['peak'])) {
399
                    $this->info['replay_gain']['album']['max_noclip_gain'] = 0 - 20 * log10($this->info['replay_gain']['album']['peak']);
400
                }
401
            }
402
403
404
            // ProcessAudioStreams() {
405
            if (@!$this->info['audio']['streams'] && (@$this->info['audio']['bitrate'] || @$this->info['audio']['channels'] || @$this->info['audio']['sample_rate'])) {
406
                  foreach ($this->info['audio'] as $key => $value) {
407
                    if ($key != 'streams') {
408
                        $this->info['audio']['streams'][0][$key] = $value;
409
                    }
410
                }
411
            }
412
        }
413
414
415
        // Get the md5/sha1sum of the audio/video portion of the file - without ID3/APE/Lyrics3/etc header/footer tags.
416
        if ($this->option_md5_data || $this->option_sha1_data) {
417
418
            // Load data-hash library if needed
419
            $this->include_module('lib.data_hash');
420
421
            if ($this->option_sha1_data) {
422
                new getid3_lib_data_hash($this, 'sha1');
423
            }
424
425
            if ($this->option_md5_data) {
426
427
                // no md5_data_source or option disabled -- md5_data_source supported by FLAC, MAC, OptimFROG, Wavpack4
428
                if (!$this->option_md5_data_source || !@$this->info['md5_data_source']) {
429
                    new getid3_lib_data_hash($this, 'md5');
430
                }
431
432
                // copy md5_data_source to md5_data if option set to true
433
                elseif ($this->option_md5_data_source && @$this->info['md5_data_source']) {
434
                    $this->info['md5_data'] = $this->info['md5_data_source'];
435
                }
436
            }
437
        }
438
439
        // Set warnings
440
        if ($this->warnings) {
441
            $this->info['warning'] = $this->warnings;
442
        }
443
444
        // Return result
445
        return $this->info;
446
    }
447
448
449
450
    // Return array of warnings
451
    public function warnings() {
452
453
        return $this->warnings;
454
    }
455
456
457
458
    // Add warning(s) to $this->warnings[]
459
    public function warning($message) {
460
461
        if (is_array($message)) {
462
            $this->warnings = array_merge($this->warnings, $message);
463
        }
464
        else {
465
            $this->warnings[] = $message;
466
        }
467
    }
468
469
470
471
    //  Clear all warnings when cloning
472
    public function __clone() {
473
474
        $this->warnings = array ();
475
476
        // Copy info array, otherwise it will be a reference.
477
        $temp = $this->info;
478
        unset($this->info);
479
        $this->info = $temp;
480
    }
481
482
483
484
    // Convert string between charsets -- iconv() wrapper
485
    public function iconv($in_charset, $out_charset, $string, $drop01 = false) {
486
487
        if ($drop01 && ($string === "\x00" || $string === "\x01")) {
488
            return '';
489
        }
490
491
492
        if (!$this->iconv_present) {
493
            return getid3_iconv_replacement::iconv($in_charset, $out_charset, $string);
494
        }
495
496
497
        // iconv() present
498
        if ($result = @iconv($in_charset, $out_charset.'//TRANSLIT', $string)) {
499
500
            if ($out_charset == 'ISO-8859-1') {
501
                return rtrim($result, "\x00");
502
            }
503
            return $result;
504
        }
505
506
        $this->warning('iconv() was unable to convert the string: "' . $string . '" from ' . $in_charset . ' to ' . $out_charset);
507
        return $string;
508
    }
509
510
511
512
    public function include_module($name) {
513
514
        if (!file_exists($this->include_path.'module.'.$name.'.php')) {
515
            throw new getid3_exception('Required module.'.$name.'.php is missing.');
516
        }
517
518
        include_once($this->include_path.'module.'.$name.'.php');
519
    }
520
521
522
523
    public function include_module_optional($name) {
524
525
        if (!file_exists($this->include_path.'module.'.$name.'.php')) {
526
            return;
527
        }
528
529
        include_once($this->include_path.'module.'.$name.'.php');
530
        return true;
531
    }
532
533
534
    // Return array containing information about all supported formats
535
    public static function GetFileFormatArray() {
536
537
        static $format_info = array (
538
539
                // Audio formats
540
541
                // AC-3   - audio      - Dolby AC-3 / Dolby Digital
542
                'ac3'  => array (
543
                            'pattern'   => '^\x0B\x77',
544
                            'group'     => 'audio',
545
                            'module'    => 'ac3',
546
                            'mime_type' => 'audio/ac3',
547
                          ),
548
549
                // AAC  - audio       - Advanced Audio Coding (AAC) - ADIF format
550
                'adif' => array (
551
                            'pattern'   => '^ADIF',
552
                            'group'     => 'audio',
553
                            'module'    => 'aac_adif',
554
                            'mime_type' => 'application/octet-stream',
555
                            'fail_ape'  => 'WARNING',
556
                          ),
557
558
559
                // AAC  - audio       - Advanced Audio Coding (AAC) - ADTS format (very similar to MP3)
560
                'adts' => array (
561
                            'pattern'   => '^\xFF[\xF0-\xF1\xF8-\xF9]',
562
                            'group'     => 'audio',
563
                            'module'    => 'aac_adts',
564
                            'mime_type' => 'application/octet-stream',
565
                            'fail_ape'  => 'WARNING',
566
                          ),
567
568
569
                // AU   - audio       - NeXT/Sun AUdio (AU)
570
                'au'   => array (
571
                            'pattern'   => '^\.snd',
572
                            'group'     => 'audio',
573
                            'module'    => 'au',
574
                            'mime_type' => 'audio/basic',
575
                          ),
576
577
                // AVR  - audio       - Audio Visual Research
578
                'avr'  => array (
579
                            'pattern'   => '^2BIT',
580
                            'group'     => 'audio',
581
                            'module'    => 'avr',
582
                            'mime_type' => 'application/octet-stream',
583
                          ),
584
585
                // BONK - audio       - Bonk v0.9+
586
                'bonk' => array (
587
                            'pattern'   => '^\x00(BONK|INFO|META| ID3)',
588
                            'group'     => 'audio',
589
                            'module'    => 'bonk',
590
                            'mime_type' => 'audio/xmms-bonk',
591
                          ),
592
593
                // DTS  - audio       - Dolby Theatre System
594
				'dts'  => array(
595
							'pattern'   => '^\x7F\xFE\x80\x01',
596
							'group'     => 'audio',
597
							'module'    => 'dts',
598
							'mime_type' => 'audio/dts',
599
						),
600
601
                // FLAC - audio       - Free Lossless Audio Codec
602
                'flac' => array (
603
                            'pattern'   => '^fLaC',
604
                            'group'     => 'audio',
605
                            'module'    => 'xiph',
606
                            'mime_type' => 'audio/x-flac',
607
                          ),
608
609
                // LA   - audio       - Lossless Audio (LA)
610
                'la'   => array (
611
                            'pattern'   => '^LA0[2-4]',
612
                            'group'     => 'audio',
613
                            'module'    => 'la',
614
                            'mime_type' => 'application/octet-stream',
615
                          ),
616
617
                // LPAC - audio       - Lossless Predictive Audio Compression (LPAC)
618
                'lpac' => array (
619
                            'pattern'   => '^LPAC',
620
                            'group'     => 'audio',
621
                            'module'    => 'lpac',
622
                            'mime_type' => 'application/octet-stream',
623
                          ),
624
625
                // MIDI - audio       - MIDI (Musical Instrument Digital Interface)
626
                'midi' => array (
627
                            'pattern'   => '^MThd',
628
                            'group'     => 'audio',
629
                            'module'    => 'midi',
630
                            'mime_type' => 'audio/midi',
631
                          ),
632
633
                // MAC  - audio       - Monkey's Audio Compressor
634
                'mac'  => array (
635
                            'pattern'   => '^MAC ',
636
                            'group'     => 'audio',
637
                            'module'    => 'monkey',
638
                            'mime_type' => 'application/octet-stream',
639
                          ),
640
641
                // MOD  - audio       - MODule (assorted sub-formats)
642
                'mod'  => array (
643
                            'pattern'   => '^.{1080}(M.K.|[5-9]CHN|[1-3][0-9]CH)',
644
                            'mime_type' => 'audio/mod',
645
                          ),
646
647
                // MOD  - audio       - MODule (Impulse Tracker)
648
                'it'   => array (
649
                            'pattern'   => '^IMPM',
650
                            'mime_type' => 'audio/it',
651
                          ),
652
653
                // MOD  - audio       - MODule (eXtended Module, various sub-formats)
654
                'xm'   => array (
655
                            'pattern'   => '^Extended Module',
656
                            'mime_type' => 'audio/xm',
657
                          ),
658
659
                // MOD  - audio       - MODule (ScreamTracker)
660
                's3m'  => array (
661
                            'pattern'   => '^.{44}SCRM',
662
                            'mime_type' => 'audio/s3m',
663
                          ),
664
665
                // MPC  - audio       - Musepack / MPEGplus SV7+
666
                'mpc'  => array (
667
                            'pattern'   => '^(MP\+)',
668
                            'group'     => 'audio',
669
                            'module'    => 'mpc',
670
                            'mime_type' => 'audio/x-musepack',
671
                          ),
672
673
                // MPC  - audio       - Musepack / MPEGplus SV4-6
674
                'mpc_old' => array (
675
                            'pattern'   => '^([\x00\x01\x10\x11\x40\x41\x50\x51\x80\x81\x90\x91\xC0\xC1\xD0\xD1][\x20-37][\x00\x20\x40\x60\x80\xA0\xC0\xE0])',
676
                            'group'     => 'audio',
677
                            'module'    => 'mpc_old',
678
                            'mime_type' => 'application/octet-stream',
679
                          ),
680
681
682
                // MP3  - audio       - MPEG-audio Layer 3 (very similar to AAC-ADTS)
683
                'mp3'  => array (
684
                            'pattern'   => '^\xFF[\xE2-\xE7\xF2-\xF7\xFA-\xFF][\x00-\xEB]',
685
                            'group'     => 'audio',
686
                            'module'    => 'mp3',
687
                            'mime_type' => 'audio/mpeg',
688
                          ),
689
690
                // OFR  - audio       - OptimFROG
691
                'ofr'  => array (
692
                            'pattern'   => '^(\*RIFF|OFR)',
693
                            'group'     => 'audio',
694
                            'module'    => 'optimfrog',
695
                            'mime_type' => 'application/octet-stream',
696
                          ),
697
698
                // RKAU - audio       - RKive AUdio compressor
699
                'rkau' => array (
700
                            'pattern'   => '^RKA',
701
                            'group'     => 'audio',
702
                            'module'    => 'rkau',
703
                            'mime_type' => 'application/octet-stream',
704
                          ),
705
706
                // SHN  - audio       - Shorten
707
                'shn'  => array (
708
                            'pattern'   => '^ajkg',
709
                            'group'     => 'audio',
710
                            'module'    => 'shorten',
711
                            'mime_type' => 'audio/xmms-shn',
712
                            'fail_id3'  => 'ERROR',
713
                            'fail_ape'  => 'ERROR',
714
                          ),
715
716
                // TTA  - audio       - TTA Lossless Audio Compressor (http://tta.corecodec.org)
717
                'tta'  => array (
718
                            'pattern'   => '^TTA',  // could also be '^TTA(\x01|\x02|\x03|2|1)'
719
                            'group'     => 'audio',
720
                            'module'    => 'tta',
721
                            'mime_type' => 'application/octet-stream',
722
                          ),
723
724
                // VOC  - audio       - Creative Voice (VOC)
725
                'voc'  => array (
726
                            'pattern'   => '^Creative Voice File',
727
                            'group'     => 'audio',
728
                            'module'    => 'voc',
729
                            'mime_type' => 'audio/voc',
730
                          ),
731
732
                // VQF  - audio       - transform-domain weighted interleave Vector Quantization Format (VQF)
733
                'vqf'  => array (
734
                            'pattern'   => '^TWIN',
735
                            'group'     => 'audio',
736
                            'module'    => 'vqf',
737
                            'mime_type' => 'application/octet-stream',
738
                          ),
739
740
                // WV  - audio        - WavPack (v4.0+)
741
                'vw'  => array(
742
                            'pattern'   => '^wvpk',
743
                            'group'     => 'audio',
744
                            'module'    => 'wavpack',
745
                            'mime_type' => 'application/octet-stream',
746
                          ),
747
748
749
                // Audio-Video formats
750
751
                // ASF  - audio/video - Advanced Streaming Format, Windows Media Video, Windows Media Audio
752
                'asf'  => array (
753
                            'pattern'   => '^\x30\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C',
754
                            'group'     => 'audio-video',
755
                            'module'    => 'asf',
756
                            'mime_type' => 'video/x-ms-asf',
757
                          ),
758
759
                // BINK  - audio/video - Bink / Smacker
760
                'bink' => array(
761
                            'pattern'   => '^(BIK|SMK)',
762
                            'mime_type' => 'application/octet-stream',
763
                          ),
764
765
                // FLV  - audio/video - FLash Video
766
                'flv' => array(
767
                            'pattern'   => '^FLV\x01',
768
                            'group'     => 'audio-video',
769
                            'module'    => 'flv',
770
                            'mime_type' => 'video/x-flv',
771
                          ),
772
773
                // MKAV - audio/video - Mastroka
774
                'matroska' => array (
775
                            'pattern'   => '^\x1A\x45\xDF\xA3',
776
                            'mime_type' => 'application/octet-stream',
777
                          ),
778
779
                // MPEG - audio/video - MPEG (Moving Pictures Experts Group)
780
                'mpeg' => array (
781
                            'pattern'   => '^\x00\x00\x01(\xBA|\xB3)',
782
                            'group'     => 'audio-video',
783
                            'module'    => 'mpeg',
784
                            'mime_type' => 'video/mpeg',
785
                          ),
786
787
                // NSV  - audio/video - Nullsoft Streaming Video (NSV)
788
                'nsv'  => array (
789
                            'pattern'   => '^NSV[sf]',
790
                            'group'     => 'audio-video',
791
                            'module'    => 'nsv',
792
                            'mime_type' => 'application/octet-stream',
793
                          ),
794
795
                // Ogg  - audio/video - Ogg (Ogg Vorbis, OggFLAC, Speex, Ogg Theora(*), Ogg Tarkin(*))
796
                'ogg'  => array (
797
                            'pattern'   => '^OggS',
798
                            'group'     => 'audio',
799
                            'module'    => 'xiph',
800
                            'mime_type' => 'application/ogg',
801
                            'fail_id3'  => 'WARNING',
802
                            'fail_ape'  => 'WARNING',
803
                          ),
804
805
                // QT   - audio/video - Quicktime
806
                'quicktime' => array (
807
                            'pattern'   => '^.{4}(cmov|free|ftyp|mdat|moov|pnot|skip|wide)',
808
                            'group'     => 'audio-video',
809
                            'module'    => 'quicktime',
810
                            'mime_type' => 'video/quicktime',
811
                          ),
812
813
                // RIFF - audio/video - Resource Interchange File Format (RIFF) / WAV / AVI / CD-audio / SDSS = renamed variant used by SmartSound QuickTracks (www.smartsound.com) / FORM = Audio Interchange File Format (AIFF)
814
                'riff' => array (
815
                            'pattern'   => '^(RIFF|SDSS|FORM)',
816
                            'group'     => 'audio-video',
817
                            'module'    => 'riff',
818
                            'mime_type' => 'audio/x-wave',
819
                            'fail_ape'  => 'WARNING',
820
                          ),
821
822
                // Real - audio/video - RealAudio, RealVideo
823
                'real' => array (
824
                            'pattern'   => '^(\.RMF|.ra)',
825
                            'group'     => 'audio-video',
826
                            'module'    => 'real',
827
                            'mime_type' => 'audio/x-realaudio',
828
                          ),
829
830
                // SWF - audio/video - ShockWave Flash
831
                'swf' => array (
832
                            'pattern'   => '^(F|C)WS',
833
                            'group'     => 'audio-video',
834
                            'module'    => 'swf',
835
                            'mime_type' => 'application/x-shockwave-flash',
836
                          ),
837
838
839
                // Still-Image formats
840
841
                // BMP  - still image - Bitmap (Windows, OS/2; uncompressed, RLE8, RLE4)
842
                'bmp'  => array (
843
                            'pattern'   => '^BM',
844
                            'group'     => 'graphic',
845
                            'module'    => 'bmp',
846
                            'mime_type' => 'image/bmp',
847
                            'fail_id3'  => 'ERROR',
848
                            'fail_ape'  => 'ERROR',
849
                          ),
850
851
                // GIF  - still image - Graphics Interchange Format
852
                'gif'  => array (
853
                            'pattern'   => '^GIF',
854
                            'group'     => 'graphic',
855
                            'module'    => 'gif',
856
                            'mime_type' => 'image/gif',
857
                            'fail_id3'  => 'ERROR',
858
                            'fail_ape'  => 'ERROR',
859
                          ),
860
861
                // JPEG - still image - Joint Photographic Experts Group (JPEG)
862
                'jpeg'  => array (
863
                            'pattern'   => '^\xFF\xD8\xFF',
864
                            'group'     => 'graphic',
865
                            'module'    => 'jpeg',
866
                            'mime_type' => 'image/jpeg',
867
                            'fail_id3'  => 'ERROR',
868
                            'fail_ape'  => 'ERROR',
869
                          ),
870
871
                // PCD  - still image - Kodak Photo CD
872
                'pcd'  => array (
873
                            'pattern'   => '^.{2048}PCD_IPI\x00',
874
                            'group'     => 'graphic',
875
                            'module'    => 'pcd',
876
                            'mime_type' => 'image/x-photo-cd',
877
                            'fail_id3'  => 'ERROR',
878
                            'fail_ape'  => 'ERROR',
879
                          ),
880
881
882
                // PNG  - still image - Portable Network Graphics (PNG)
883
                'png'  => array (
884
                            'pattern'   => '^\x89\x50\x4E\x47\x0D\x0A\x1A\x0A',
885
                            'group'     => 'graphic',
886
                            'module'    => 'png',
887
                            'mime_type' => 'image/png',
888
                            'fail_id3'  => 'ERROR',
889
                            'fail_ape'  => 'ERROR',
890
                          ),
891
892
893
                // SVG  - still image - Scalable Vector Graphics (SVG)
894
				'svg'  => array(
895
							'pattern'   => '<!DOCTYPE svg PUBLIC ',
896
							'mime_type' => 'image/svg+xml',
897
							'fail_id3'  => 'ERROR',
898
							'fail_ape'  => 'ERROR',
899
						),
900
901
902
                // TIFF  - still image - Tagged Information File Format (TIFF)
903
                'tiff' => array (
904
                            'pattern'   => '^(II\x2A\x00|MM\x00\x2A)',
905
                            'group'     => 'graphic',
906
                            'module'    => 'tiff',
907
                            'mime_type' => 'image/tiff',
908
                            'fail_id3'  => 'ERROR',
909
                            'fail_ape'  => 'ERROR',
910
                          ),
911
912
913
                // Data formats
914
915
                'exe'  => array(
916
                            'pattern'   => '^MZ',
917
                            'mime_type' => 'application/octet-stream',
918
                            'fail_id3'  => 'ERROR',
919
                            'fail_ape'  => 'ERROR',
920
                          ),
921
922
                // ISO  - data        - International Standards Organization (ISO) CD-ROM Image
923
                'iso'  => array (
924
                            'pattern'   => '^.{32769}CD001',
925
                            'group'     => 'misc',
926
                            'module'    => 'iso',
927
                            'mime_type' => 'application/octet-stream',
928
                            'fail_id3'  => 'ERROR',
929
                            'fail_ape'  => 'ERROR',
930
                          ),
931
932
                // RAR  - data        - RAR compressed data
933
                'rar'  => array(
934
                            'pattern'   => '^Rar\!',
935
                            'mime_type' => 'application/octet-stream',
936
                            'fail_id3'  => 'ERROR',
937
                            'fail_ape'  => 'ERROR',
938
                          ),
939
940
                // SZIP - audio       - SZIP compressed data
941
                'szip' => array (
942
                            'pattern'   => '^SZ\x0A\x04',
943
                            'group'     => 'archive',
944
                            'module'    => 'szip',
945
                            'mime_type' => 'application/octet-stream',
946
                            'fail_id3'  => 'ERROR',
947
                            'fail_ape'  => 'ERROR',
948
                          ),
949
950
                // TAR  - data        - TAR compressed data
951
                'tar'  => array(
952
                            'pattern'   => '^.{100}[0-9\x20]{7}\x00[0-9\x20]{7}\x00[0-9\x20]{7}\x00[0-9\x20\x00]{12}[0-9\x20\x00]{12}',
953
                            'group'     => 'archive',
954
                            'module'    => 'tar',
955
                            'mime_type' => 'application/x-tar',
956
                            'fail_id3'  => 'ERROR',
957
                            'fail_ape'  => 'ERROR',
958
                          ),
959
960
                // GZIP  - data        - GZIP compressed data
961
                'gz'  => array(
962
                            'pattern'   => '^\x1F\x8B\x08',
963
                            'group'     => 'archive',
964
                            'module'    => 'gzip',
965
                            'mime_type' => 'application/x-gzip',
966
                            'fail_id3'  => 'ERROR',
967
                            'fail_ape'  => 'ERROR',
968
                          ),
969
970
971
                // ZIP  - data        - ZIP compressed data
972
                'zip'  => array (
973
                            'pattern'   => '^PK\x03\x04',
974
                            'group'     => 'archive',
975
                            'module'    => 'zip',
976
                            'mime_type' => 'application/zip',
977
                            'fail_id3'  => 'ERROR',
978
                            'fail_ape'  => 'ERROR',
979
                          ),
980
981
982
                // PAR2 - data        - Parity Volume Set Specification 2.0
983
                'par2' => array (
984
                			'pattern'   => '^PAR2\x00PKT',
985
							'mime_type' => 'application/octet-stream',
986
							'fail_id3'  => 'ERROR',
987
							'fail_ape'  => 'ERROR',
988
						),
989
990
991
                 // PDF  - data       - Portable Document Format
992
                 'pdf' => array(
993
                            'pattern'   => '^\x25PDF',
994
                            'mime_type' => 'application/pdf',
995
                            'fail_id3'  => 'ERROR',
996
                            'fail_ape'  => 'ERROR',
997
                           ),
998
999
                 // DOC  - data       - Microsoft Word
1000
                 'msoffice' => array(
1001
                            'pattern'   => '^\xD0\xCF\x11\xE0', // D0CF11E == DOCFILE == Microsoft Office Document
1002
                            'mime_type' => 'application/octet-stream',
1003
                            'fail_id3'  => 'ERROR',
1004
                            'fail_ape'  => 'ERROR',
1005
                          ),
1006
            );
1007
1008
        return $format_info;
1009
    }
1010
1011
1012
1013
    // Recursive over array - converts array to $encoding charset from $this->encoding
1014
    function CharConvert(&$array, $encoding) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1015
1016
        // Identical encoding - end here
1017
        if ($encoding == $this->encoding) {
1018
            return;
1019
        }
1020
1021
        // Loop thru array
1022
        foreach ($array as $key => $value) {
1023
1024
            // Go recursive
1025
            if (is_array($value)) {
1026
                $this->CharConvert($array[$key], $encoding);
1027
            }
1028
1029
            // Convert string
1030
            elseif (is_string($value)) {
1031
                $array[$key] = $this->iconv($encoding, $this->encoding, $value);
1032
            }
1033
        }
1034
    }
1035
1036
1037
1038
    // Convert and copy tags
1039
    protected function HandleAllTags() {
1040
1041
        // Key name => array (tag name, character encoding)
1042
        static $tags = array (
1043
            'asf'       => array ('asf',           'UTF-16LE'),
1044
            'midi'      => array ('midi',          'ISO-8859-1'),
1045
            'nsv'       => array ('nsv',           'ISO-8859-1'),
1046
            'ogg'       => array ('vorbiscomment', 'UTF-8'),
1047
            'png'       => array ('png',           'UTF-8'),
1048
            'tiff'      => array ('tiff',          'ISO-8859-1'),
1049
            'quicktime' => array ('quicktime',     'ISO-8859-1'),
1050
            'real'      => array ('real',          'ISO-8859-1'),
1051
            'vqf'       => array ('vqf',           'ISO-8859-1'),
1052
            'zip'       => array ('zip',           'ISO-8859-1'),
1053
            'riff'      => array ('riff',          'ISO-8859-1'),
1054
            'lyrics3'   => array ('lyrics3',       'ISO-8859-1'),
1055
            'id3v1'     => array ('id3v1',         ''),            // change below - cannot assign variable to static array
1056
            'id3v2'     => array ('id3v2',         'UTF-8'),       // module converts all frames to UTF-8
1057
            'ape'       => array ('ape',           'UTF-8')
1058
        );
1059
        $tags['id3v1'][1] = $this->encoding_id3v1;
1060
1061
        // Loop thru tags array
1062
        foreach ($tags as $comment_name => $tag_name_encoding_array) {
1063
            list($tag_name, $encoding) = $tag_name_encoding_array;
1064
1065
            // Fill in default encoding type if not already present
1066
            @$this->info[$comment_name]  and  $this->info[$comment_name]['encoding'] = $encoding;
1067
1068
            // Copy comments if key name set
1069
            if (@$this->info[$comment_name]['comments']) {
1070
1071
                foreach ($this->info[$comment_name]['comments'] as $tag_key => $value_array) {
1072
                    foreach ($value_array as $key => $value) {
1073
                        if (strlen(trim($value)) > 0) {
1074
                            $this->info['tags'][$tag_name][trim($tag_key)][] = $value; // do not trim!! Unicode characters will get mangled if trailing nulls are removed!
1075
                        }
1076
                    }
1077
1078
                }
1079
1080
                if (!@$this->info['tags'][$tag_name]) {
1081
                    // comments are set but contain nothing but empty strings, so skip
1082
                    continue;
1083
                }
1084
1085
                $this->CharConvert($this->info['tags'][$tag_name], $encoding);
1086
            }
1087
        }
1088
1089
1090
        // Merge comments from ['tags'] into common ['comments']
1091
        if (@$this->info['tags']) {
1092
1093
            foreach ($this->info['tags'] as $tag_type => $tag_array) {
1094
1095
                foreach ($tag_array as $tag_name => $tagdata) {
1096
1097
                    foreach ($tagdata as $key => $value) {
1098
1099
                        if (!empty($value)) {
1100
1101
                            if (empty($this->info['comments'][$tag_name])) {
1102
1103
                                // fall through and append value
1104
                            }
1105
                            elseif ($tag_type == 'id3v1') {
1106
1107
                                $new_value_length = strlen(trim($value));
1108
                                foreach ($this->info['comments'][$tag_name] as $existing_key => $existing_value) {
1109
                                    $old_value_length = strlen(trim($existing_value));
1110
                                    if (($new_value_length <= $old_value_length) && (substr($existing_value, 0, $new_value_length) == trim($value))) {
1111
                                        // new value is identical but shorter-than (or equal-length to) one already in comments - skip
1112
                                        break 2;
1113
                                    }
1114
                                }
1115
                            }
1116
                            else {
1117
1118
                                $new_value_length = strlen(trim($value));
1119
                                foreach ($this->info['comments'][$tag_name] as $existing_key => $existing_value) {
1120
                                    $old_value_length = strlen(trim($existing_value));
1121
                                    if (($new_value_length > $old_value_length) && (substr(trim($value), 0, strlen($existing_value)) == $existing_value)) {
1122
                                        $this->info['comments'][$tag_name][$existing_key] = trim($value);
1123
                                        break 2;
1124
                                    }
1125
                                }
1126
                            }
1127
1128
                            if (empty($this->info['comments'][$tag_name]) || !in_array(trim($value), $this->info['comments'][$tag_name])) {
1129
                                $this->info['comments'][$tag_name][] = trim($value);
1130
                            }
1131
                        }
1132
                    }
1133
                }
1134
            }
1135
        }
1136
1137
        return true;
1138
    }
1139
}
1140
1141
1142
abstract class getid3_handler
1143
{
1144
1145
    protected $getid3;                          // pointer
1146
1147
    protected $data_string_flag = false;        // analyzing filepointer or string
1148
    protected $data_string;                     // string to analyze
1149
    protected $data_string_position = 0;        // seek position in string
1150
1151
1152
    public function __construct(getID3 $getid3) {
1153
1154
        $this->getid3 = $getid3;
1155
    }
1156
1157
1158
    // Analyze from file pointer
1159
    abstract public function Analyze();
1160
1161
1162
1163
    // Analyze from string instead
1164
    public function AnalyzeString(&$string) {
1165
1166
        // Enter string mode
1167
        $this->data_string_flag = true;
1168
        $this->data_string      = $string;
1169
1170
        // Save info
1171
        $saved_avdataoffset = $this->getid3->info['avdataoffset'];
1172
        $saved_avdataend    = $this->getid3->info['avdataend'];
1173
        $saved_filesize     = $this->getid3->info['filesize'];
1174
1175
        // Reset some info
1176
        $this->getid3->info['avdataoffset'] = 0;
1177
        $this->getid3->info['avdataend']    = $this->getid3->info['filesize'] = strlen($string);
1178
1179
        // Analyze
1180
        $this->Analyze();
1181
1182
        // Restore some info
1183
        $this->getid3->info['avdataoffset'] = $saved_avdataoffset;
1184
        $this->getid3->info['avdataend']    = $saved_avdataend;
1185
        $this->getid3->info['filesize']     = $saved_filesize;
1186
1187
        // Exit string mode
1188
        $this->data_string_flag = false;
1189
    }
1190
1191
1192
    protected function ftell() {
1193
1194
        if ($this->data_string_flag) {
1195
            return $this->data_string_position;
1196
        }
1197
        return ftell($this->getid3->fp);
1198
    }
1199
1200
1201
    protected function fread($bytes) {
1202
1203
        if ($this->data_string_flag) {
1204
            $this->data_string_position += $bytes;
1205
            return substr($this->data_string, $this->data_string_position - $bytes, $bytes);
1206
        }
1207
        return fread($this->getid3->fp, $bytes);
1208
    }
1209
1210
1211
    protected function fseek($bytes, $whence = SEEK_SET) {
1212
1213
        if ($this->data_string_flag) {
1214
            switch ($whence) {
1215
                case SEEK_SET:
1216
                    $this->data_string_position = $bytes;
1217
                    return;
1218
1219
                case SEEK_CUR:
1220
                    $this->data_string_position += $bytes;
1221
                    return;
1222
1223
                case SEEK_END:
1224
                    $this->data_string_position = strlen($this->data_string) + $bytes;
1225
                    return;
1226
            }
1227
        }
1228
        return fseek($this->getid3->fp, $bytes, $whence);
1229
    }
1230
1231
}
1232
1233
1234
1235
1236
abstract class getid3_handler_write
1237
{
1238
    protected $filename;
1239
    protected $user_abort;
1240
1241
    private $fp_lock;
1242
    private $owner;
1243
    private $group;
1244
    private $perms;
1245
1246
1247
    public function __construct($filename) {
1248
1249
        if (!file_exists($filename)) {
1250
            throw new getid3_exception('File does not exist: "' . $filename . '"');
1251
        }
1252
1253
        if (!is_writeable($filename)) {
1254
            throw new getid3_exception('File is not writeable: "' . $filename . '"');
1255
        }
1256
1257
        if (!is_writeable(dirname($filename))) {
1258
            throw new getid3_exception('Directory is not writeable: ' . dirname($filename) . ' (need to write lock file).');
1259
        }
1260
1261
        $this->user_abort = ignore_user_abort(true);
1262
1263
        $this->fp_lock = fopen($filename . '.getid3.lock', 'w');
1264
        flock($this->fp_lock, LOCK_EX);
1265
1266
        $this->filename = $filename;
1267
    }
1268
1269
1270
    public function __destruct() {
1271
1272
        flock($this->fp_lock, LOCK_UN);
1273
        fclose($this->fp_lock);
1274
        unlink($this->filename . '.getid3.lock');
1275
1276
        ignore_user_abort($this->user_abort);
1277
    }
1278
    
1279
    
1280
    protected function save_permissions() {
1281
        
1282
        $this->owner = fileowner($this->filename);
1283
        $this->group = filegroup($this->filename);
1284
        $this->perms = fileperms($this->filename);
1285
    }
1286
    
1287
    
1288
    protected function restore_permissions() {
1289
        
1290
        @chown($this->filename, $this->owner);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for chown(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

1290
        /** @scrutinizer ignore-unhandled */ @chown($this->filename, $this->owner);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1291
        @chgrp($this->filename, $this->group);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for chgrp(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

1291
        /** @scrutinizer ignore-unhandled */ @chgrp($this->filename, $this->group);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1292
        @chmod($this->filename, $this->perms);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for chmod(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

1292
        /** @scrutinizer ignore-unhandled */ @chmod($this->filename, $this->perms);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1293
    }
1294
1295
1296
    abstract public function read();
1297
1298
    abstract public function write();
1299
1300
    abstract public function remove();
1301
1302
}
1303
1304
1305
1306
1307
class getid3_exception extends Exception
1308
{
1309
    public $message;
1310
1311
}
1312
1313
1314
1315
1316
class getid3_lib
1317
{
1318
1319
    // Convert Little Endian byte string to int - max 32 bits
1320
    public static function LittleEndian2Int($byte_word, $signed = false) {
1321
1322
        return getid3_lib::BigEndian2Int(strrev($byte_word), $signed);
1323
    }
1324
1325
1326
1327
    // Convert number to Little Endian byte string
1328
    public static function LittleEndian2String($number, $minbytes=1, $synchsafe=false) {
1329
        $intstring = '';
1330
        while ($number > 0) {
1331
            if ($synchsafe) {
1332
                $intstring = $intstring.chr($number & 127);
1333
                $number >>= 7;
1334
            } else {
1335
                $intstring = $intstring.chr($number & 255);
1336
                $number >>= 8;
1337
            }
1338
        }
1339
        return str_pad($intstring, $minbytes, "\x00", STR_PAD_RIGHT);
1340
    }
1341
1342
1343
1344
    // Convert Big Endian byte string to int - max 32 bits
1345
    public static function BigEndian2Int($byte_word, $signed = false) {
1346
1347
        $int_value = 0;
1348
        $byte_wordlen = strlen($byte_word);
1349
1350
        for ($i = 0; $i < $byte_wordlen; $i++) {
1351
            $int_value += ord($byte_word{$i}) * pow(256, ($byte_wordlen - 1 - $i));
1352
        }
1353
1354
        if ($signed) {
1355
            $sign_mask_bit = 0x80 << (8 * ($byte_wordlen - 1));
1356
            if ($int_value & $sign_mask_bit) {
1357
                $int_value = 0 - ($int_value & ($sign_mask_bit - 1));
1358
            }
1359
        }
1360
1361
        return $int_value;
1362
    }
1363
1364
1365
1366
    // Convert Big Endian byte sybc safe string to int - max 32 bits
1367
    public static function BigEndianSyncSafe2Int($byte_word) {
1368
1369
        $int_value = 0;
1370
        $byte_wordlen = strlen($byte_word);
1371
1372
        // disregard MSB, effectively 7-bit bytes
1373
        for ($i = 0; $i < $byte_wordlen; $i++) {
1374
            $int_value = $int_value | (ord($byte_word{$i}) & 0x7F) << (($byte_wordlen - 1 - $i) * 7);
1375
        }
1376
        return $int_value;
1377
    }
1378
1379
1380
1381
    // Convert Big Endian byte string to bit string
1382
    public static function BigEndian2Bin($byte_word) {
1383
1384
        $bin_value = '';
1385
        $byte_wordlen = strlen($byte_word);
1386
        for ($i = 0; $i < $byte_wordlen; $i++) {
1387
            $bin_value .= str_pad(decbin(ord($byte_word{$i})), 8, '0', STR_PAD_LEFT);
1388
        }
1389
        return $bin_value;
1390
    }
1391
1392
1393
1394
    public static function BigEndian2Float($byte_word) {
1395
1396
		// ANSI/IEEE Standard 754-1985, Standard for Binary Floating Point Arithmetic
1397
		// http://www.psc.edu/general/software/packages/ieee/ieee.html
1398
		// http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee.html
1399
1400
		$bit_word = getid3_lib::BigEndian2Bin($byte_word);
1401
		if (!$bit_word) {
1402
            return 0;
1403
        }
1404
		$sign_bit = $bit_word{0};
1405
1406
		switch (strlen($byte_word) * 8) {
1407
			case 32:
1408
				$exponent_bits = 8;
1409
				$fraction_bits = 23;
1410
				break;
1411
1412
			case 64:
1413
				$exponent_bits = 11;
1414
				$fraction_bits = 52;
1415
				break;
1416
1417
			case 80:
1418
				// 80-bit Apple SANE format
1419
				// http://www.mactech.com/articles/mactech/Vol.06/06.01/SANENormalized/
1420
				$exponent_string = substr($bit_word, 1, 15);
1421
				$is_normalized = intval($bit_word{16});
1422
				$fraction_string = substr($bit_word, 17, 63);
1423
				$exponent = pow(2, getid3_lib::Bin2Dec($exponent_string) - 16383);
1424
				$fraction = $is_normalized + getid3_lib::DecimalBinary2Float($fraction_string);
1425
				$float_value = $exponent * $fraction;
1426
				if ($sign_bit == '1') {
1427
					$float_value *= -1;
1428
				}
1429
				return $float_value;
1430
				break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
1431
1432
			default:
1433
				return false;
1434
				break;
1435
		}
1436
		$exponent_string = substr($bit_word, 1, $exponent_bits);
1437
		$fraction_string = substr($bit_word, $exponent_bits + 1, $fraction_bits);
1438
		$exponent = bindec($exponent_string);
1439
		$fraction = bindec($fraction_string);
1440
1441
		if (($exponent == (pow(2, $exponent_bits) - 1)) && ($fraction != 0)) {
1442
			// Not a Number
1443
			$float_value = false;
1444
		} elseif (($exponent == (pow(2, $exponent_bits) - 1)) && ($fraction == 0)) {
1445
			if ($sign_bit == '1') {
1446
				$float_value = '-infinity';
1447
			} else {
1448
				$float_value = '+infinity';
1449
			}
1450
		} elseif (($exponent == 0) && ($fraction == 0)) {
1451
			if ($sign_bit == '1') {
1452
				$float_value = -0;
0 ignored issues
show
Unused Code introduced by
The assignment to $float_value is dead and can be removed.
Loading history...
1453
			} else {
1454
				$float_value = 0;
1455
			}
1456
			$float_value = ($sign_bit ? 0 : -0);
1457
		} elseif (($exponent == 0) && ($fraction != 0)) {
1458
			// These are 'unnormalized' values
1459
			$float_value = pow(2, (-1 * (pow(2, $exponent_bits - 1) - 2))) * getid3_lib::DecimalBinary2Float($fraction_string);
1460
			if ($sign_bit == '1') {
1461
				$float_value *= -1;
1462
			}
1463
		} elseif ($exponent != 0) {
1464
			$float_value = pow(2, ($exponent - (pow(2, $exponent_bits - 1) - 1))) * (1 + getid3_lib::DecimalBinary2Float($fraction_string));
1465
			if ($sign_bit == '1') {
1466
				$float_value *= -1;
1467
			}
1468
		}
1469
		return (float) $float_value;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $float_value does not seem to be defined for all execution paths leading up to this point.
Loading history...
1470
	}
1471
1472
1473
1474
	public static function LittleEndian2Float($byte_word) {
1475
1476
		return getid3_lib::BigEndian2Float(strrev($byte_word));
1477
	}
1478
1479
1480
1481
	public static function DecimalBinary2Float($binary_numerator) {
1482
		$numerator   = bindec($binary_numerator);
1483
		$denominator = bindec('1'.str_repeat('0', strlen($binary_numerator)));
1484
		return ($numerator / $denominator);
1485
	}
1486
1487
1488
	public static function PrintHexBytes($string, $hex=true, $spaces=true, $html_safe=true) {
1489
1490
        $return_string = '';
1491
        for ($i = 0; $i < strlen($string); $i++) {
1492
            if ($hex) {
1493
                $return_string .= str_pad(dechex(ord($string{$i})), 2, '0', STR_PAD_LEFT);
1494
            } else {
1495
                $return_string .= ' '.(ereg("[\x20-\x7E]", $string{$i}) ? $string{$i} : '�');
0 ignored issues
show
Deprecated Code introduced by
The function ereg() has been deprecated: 5.3.0 Use preg_match() instead ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1495
                $return_string .= ' '.(/** @scrutinizer ignore-deprecated */ ereg("[\x20-\x7E]", $string{$i}) ? $string{$i} : '�');

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
1496
            }
1497
            if ($spaces) {
1498
                $return_string .= ' ';
1499
            }
1500
        }
1501
        if ($html_safe) {
1502
            $return_string = htmlentities($return_string);
1503
        }
1504
        return $return_string;
1505
    }
1506
1507
1508
1509
    // Process header data string - read several values with algorithm and add to target
1510
    //   algorithm is one one the getid3_lib::Something2Something() function names
1511
    //   parts_array is  index => length    -  $target[index] = algorithm(substring(data))
1512
    //   - OR just substring(data) if length is negative!
1513
    //  indexes == 'IGNORE**' are ignored
1514
1515
    public static function ReadSequence($algorithm, &$target, &$data, $offset, $parts_array) {
1516
1517
        // Loop thru $parts_array
1518
        foreach ($parts_array as $target_string => $length) {
1519
1520
            // Add to target
1521
            if (!strstr($target_string, 'IGNORE')) {
1522
1523
                // substr(....length)
1524
                if ($length < 0) {
1525
                    $target[$target_string] = substr($data, $offset, -$length);
1526
                }
1527
1528
                // algorithm(substr(...length))
1529
                else {
1530
                    $target[$target_string] = getid3_lib::$algorithm(substr($data, $offset, $length));
1531
                }
1532
            }
1533
1534
            // Move pointer
1535
            $offset += abs($length);
1536
        }
1537
    }
1538
1539
}
1540
1541
1542
1543
class getid3_lib_replaygain
1544
{
1545
1546
    public static function NameLookup($name_code) {
1547
1548
        static $lookup = array (
1549
            0 => 'not set',
1550
            1 => 'Track Gain Adjustment',
1551
            2 => 'Album Gain Adjustment'
1552
        );
1553
1554
        return @$lookup[$name_code];
1555
    }
1556
1557
1558
1559
    public static function OriginatorLookup($originator_code) {
1560
1561
        static $lookup = array (
1562
            0 => 'unspecified',
1563
            1 => 'pre-set by artist/producer/mastering engineer',
1564
            2 => 'set by user',
1565
            3 => 'determined automatically'
1566
        );
1567
1568
        return @$lookup[$originator_code];
1569
    }
1570
1571
1572
1573
    public static function AdjustmentLookup($raw_adjustment, $sign_bit) {
1574
1575
        return (float)$raw_adjustment / 10 * ($sign_bit == 1 ? -1 : 1);
1576
    }
1577
1578
1579
1580
    public static function GainString($name_code, $originator_code, $replaygain) {
1581
1582
        $sign_bit = $replaygain < 0 ? 1 : 0;
1583
1584
        $stored_replaygain = intval(round($replaygain * 10));
1585
        $gain_string  = str_pad(decbin($name_code), 3, '0', STR_PAD_LEFT);
1586
        $gain_string .= str_pad(decbin($originator_code), 3, '0', STR_PAD_LEFT);
1587
        $gain_string .= $sign_bit;
1588
        $gain_string .= str_pad(decbin($stored_replaygain), 9, '0', STR_PAD_LEFT);
1589
1590
        return $gain_string;
1591
    }
1592
1593
}
1594
1595
1596
1597
1598
?>
0 ignored issues
show
Best Practice introduced by
It is not recommended to use PHP's closing tag ?> in files other than templates.

Using a closing tag in PHP files that only contain PHP code is not recommended as you might accidentally add whitespace after the closing tag which would then be output by PHP. This can cause severe problems, for example headers cannot be sent anymore.

A simple precaution is to leave off the closing tag as it is not required, and it also has no negative effects whatsoever.

Loading history...