Passed
Push — master ( 06f87c...33b6d3 )
by Siad
08:23
created

SizeHelper::findUnitMultiple()   A

Complexity

Conditions 5
Paths 7

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 5

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 7
nc 7
nop 1
dl 0
loc 13
ccs 8
cts 8
cp 1
crap 5
rs 9.6111
c 1
b 0
f 0
1
<?php
2
declare(strict_types=1);
3
/**
4
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
5
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
6
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
7
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
8
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
9
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
10
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
11
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
12
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
13
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
14
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
15
 *
16
 * This software consists of voluntary contributions made by many individuals
17
 * and is licensed under the LGPL. For more information please see
18
 * <http://phing.info>.
19
 */
20
21
/**
22
 * SizeHelper class
23
 *
24
 * @author Jawira Portugal <[email protected]>
25
 */
26
class SizeHelper
27
{
28
    const B    = 'B';
29
    const KILO = 1000;
30
    const KIBI = 1024;
31
    const SI   = [1 => ['kB', 'kilo', 'kilobyte',],
32
                  2 => ['MB', 'mega', 'megabyte',],
33
                  3 => ['GB', 'giga', 'gigabyte',],
34
                  4 => ['TB', 'tera', 'terabyte',],];
35
    const IEC  = [0 => [self::B,],
36
                  1 => ['k', 'Ki', 'KiB', 'kibi', 'kibibyte',],
37
                  2 => ['M', 'Mi', 'MiB', 'mebi', 'mebibyte',],
38
                  3 => ['G', 'Gi', 'GiB', 'gibi', 'gibibyte',],
39
                  4 => ['T', 'Ti', 'TiB', 'tebi', 'tebibyte',],];
40
41
    /**
42
     * Converts strings like '512K', '0.5G', '50M' to bytes.
43
     */
44 73
    public static function fromHumanToBytes(string $human): float
45
    {
46 73
        [$size, $unit] = self::parseHuman($human);
47 68
        $multiple = self::findUnitMultiple($unit);
48
49 63
        return $size * $multiple;
50
    }
51
52
    /**
53
     * Convert from bytes to any other valid unit.
54
     */
55 62
    public static function fromBytesTo(int $bytes, string $unit): float
56
    {
57 62
        $multiple = self::findUnitMultiple($unit);
58
59 51
        return $bytes / $multiple;
60
    }
61
62
    /**
63
     * Extracts size and unit from strings like '1m', '50M', '100.55K', '2048'.
64
     *
65
     * - The default unit is 'B'.
66
     * - If unit exists then it is returned as-is, even invalid units.
67
     * - This function can also handle scientific notation, e.g. '8e10k'.
68
     * - It can also handle negative values '-1M'.
69
     * - Parsing is not locale aware, this means that '.' (dot) is always used as decimal separator.
70
     *
71
     * @param string $human Filesize as a human writes it.
72
     *
73
     * @return array{0: float, 1: string} First element is size, and second is the unit.
74
     */
75 73
    protected static function parseHuman(string $human): array
76
    {
77
        // no unit, so we assume bytes
78 73
        if (is_numeric($human)) {
79 12
            return [floatval($human), self::B];
80
        }
81 61
        $parsed = sscanf($human, '%f%s');
82 61
        if (empty($parsed[0])) {
83 5
            throw new BuildException("Invalid size '$human'");
84
        }
85
86 56
        return $parsed;
87
    }
88
89
    /**
90
     * Finds the value in bytes of a single "unit".
91
     */
92 113
    protected static function findUnitMultiple(string $unit): int
93
    {
94 113
        foreach (self::IEC as $exponent => $choices) {
95 113
            if (in_array(strtolower($unit), array_map('strtolower', $choices))) {
96 76
                return pow(self::KIBI, $exponent);
97
            }
98
        }
99 39
        foreach (self::SI as $exponent => $choices) {
100 39
            if (in_array(strtolower($unit), array_map('strtolower', $choices))) {
101 23
                return pow(self::KILO, $exponent);
102
            }
103
        }
104 16
        throw new BuildException("Invalid unit '$unit'");
105
    }
106
}
107