Passed
Push — master ( 2b9668...6875d9 )
by Sebastian
02:20
created

ConvertHelper_SizeNotation::setError()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 3
c 1
b 0
f 0
nc 1
nop 2
dl 0
loc 5
rs 10
1
<?php
2
/**
3
 * File containing the {@see ConvertHelper_SizeNotation} class.
4
 * 
5
 * @package AppUtils
6
 * @subpackage ConvertHelper
7
 * @see ConvertHelper_SizeNotation
8
 */
9
10
declare(strict_types=1);
11
12
namespace AppUtils;
13
14
/**
15
 * This class is made to parse and convert storage size notations
16
 * like "50 MB" into the equivalent amount of bytes, as well as 
17
 * additional formats. It features validation, and accessing information
18
 * on the error that was detected if the parsing fails.
19
 * 
20
 * It supports both Base 10 and Base 2 size calculation.
21
 * 
22
 * @package AppUtils
23
 * @subpackage ConvertHelper
24
 * @author Sebastian Mordziol <[email protected]>
25
 */
26
class ConvertHelper_SizeNotation
27
{
28
    const VALIDATION_ERROR_MULTIPLE_UNITS = 43801;
29
    
30
    const VALIDATION_ERROR_UNRECOGNIZED_STRING = 43802;
31
    
32
    const VALIDATION_ERROR_NEGATIVE_VALUE = 43803;
33
    
34
   /**
35
    * @var string
36
    */
37
    protected $size;
38
    
39
   /**
40
    * @var integer
41
    */
42
    protected $bytes = 0;
43
    
44
   /**
45
    * @var bool
46
    */
47
    protected $valid = true;
48
    
49
   /**
50
    * @var string
51
    */
52
    protected $units = null;
53
    
54
   /**
55
    * @var string
56
    */
57
    protected $number = '';
58
    
59
   /**
60
    * @var string
61
    */
62
    protected $errorMessage = '';
63
    
64
   /**
65
    * @var int
66
    */
67
    protected $errorCode = 0;
68
    
69
   /**
70
    * Create a new instance for the specified size string.
71
    * 
72
    * @param string $size A size notation in the format "50 MB", or a number of bytes without units. Spaces are ignored, so "50MB" = "50 MB" = "  50   MB   ". Floating point values are accepted, both with dot and comma notation ("50.5" = "50,5"). To use Base 2 values, ose appropriate units, e.g. "50 MiB", "1.5 GiB". The units are case insensitive, so "50 MB" = "50 mb". 
73
    */
74
    public function __construct(string $size)
75
    {
76
        $this->size = $this->cleanString($size);
77
        
78
        $this->convert();
79
    }
80
    
81
   /**
82
    * Gets the amount of bytes contained in the size notation.
83
    * @return int
84
    */
85
    public function toBytes() : int
86
    {
87
        return $this->bytes;
88
    }
89
    
90
   /**
91
    * Converts the size notation to a human readable string, e.g. "50 MB".
92
    * 
93
    * @param int $precision
94
    * @return string
95
    * @see ConvertHelper::bytes2readable()
96
    */
97
    public function toString(int $precision=1) : string
98
    {
99
        return ConvertHelper::bytes2readable($this->bytes, $precision, $this->getBase());
100
    }
101
    
102
   /**
103
    * Retrieves the detected number's base.
104
    * @return int The base number (1000 for Base 10, or 1024 for Base 2), or 0 if it is not valid.
105
    */
106
    public function getBase() : int
107
    {
108
        if(!$this->isValid()) {
109
            return 0; 
110
        }
111
        
112
        return $this->size->getBase();
113
    }
114
    
115
   /**
116
    * Checks whether the size notation was valid and could be parsed
117
    * into a meaningful byte value. If this returns `false`, it is 
118
    * possible to use the `getErrorXXX` methods to retrieve information
119
    * on what went wrong. 
120
    * 
121
    * @return bool
122
    * @see ConvertHelper_SizeNotation::getErrorMessage()
123
    * @see ConvertHelper_SizeNotation::getErrorCode()
124
    */
125
    public function isValid() : bool
126
    {
127
        return $this->valid;
128
    }
129
    
130
   /**
131
    * Retrieves the error message if the size notation validation failed.
132
    * 
133
    * @return string
134
    * @see ConvertHelper_SizeNotation::getErrorCode()
135
    */
136
    public function getErrorMessage() : string
137
    {
138
        return $this->errorMessage;
139
    }
140
    
141
    protected function cleanString(string $string) : string
142
    {
143
        // remove spaces
144
        $result = trim(str_replace(' ', '', $string));
145
        
146
        // convert numeric notation with commas instead of dots
147
        $result = str_replace(',', '.', $result);
148
        
149
        // for case insensitivity, treat it all lowercase
150
        $result = strtolower($result);
151
        
152
        return $result;
153
    }
154
    
155
    protected function parseSize() : void
156
    {
157
        if(!$this->detectParts()) {
158
            return;
159
        }
160
        
161
        // we detected units in the string: all good.
162
        if($this->units !== null)
163
        {
164
            return;
165
        }
166
        
167
        // just a numeric value: we assume this means we're dealing with bytes.
168
        if(is_numeric($this->number)) 
169
        {
170
            $this->units = 'b';
171
            return;
172
        } 
173
        
174
        // no units found: this can be either a raw byte value,
175
        // or some other meaningless string.
176
        $this->setError(
177
            t('Unrecognized size string.'),
178
            self::VALIDATION_ERROR_UNRECOGNIZED_STRING
179
        );
180
    }
181
    
182
   /**
183
    * Detects the units and the number in the size notation string.
184
    * Populates the `units` and `number` properties.
185
    * 
186
    * @return bool Whether the string could be parsed successfully.
187
    */
188
    protected function detectParts() : bool
189
    {
190
        $units = ConvertHelper_StorageSizeEnum::getSizeNames();
191
        
192
        $number = $this->size;
193
        
194
        foreach($units as $unit)
195
        {
196
            if(stristr($number, $unit))
197
            {
198
                // there are more than 1 unit defined in the string
199
                if($this->units !== null)
200
                {
201
                    $this->setError(
202
                        t(
203
                            'Multiple units defined in the string (%1$s and %2$s).',
204
                            $this->units,
205
                            $unit
206
                        ),
207
                        self::VALIDATION_ERROR_MULTIPLE_UNITS
208
                    );
209
                    
210
                    return false;
211
                }
212
                
213
                $this->units = $unit;
214
                $number = str_replace($unit, '', $number);
215
            }
216
        }
217
        
218
        $this->number = $number;
219
        
220
        return true;
221
    }
222
    
223
   /**
224
    * If the validation fails, this is used to store the reason for retrieval later.
225
    *  
226
    * @param string $message
227
    * @param int $code
228
    * 
229
    * @see ConvertHelper_SizeNotation::isValid()
230
    * @see ConvertHelper_SizeNotation::getErrorMessage()
231
    * @see ConvertHelper_SizeNotation::getErrorCode()
232
    */
233
    protected function setError(string $message, int $code) : void
234
    {
235
        $this->valid = false;
236
        $this->errorMessage = $message;
237
        $this->errorCode = $code;
238
    }
239
    
240
   /**
241
    * Retrieves the error code, if the size notation validation failed.
242
    * 
243
    * @return int
244
    * @see ConvertHelper_SizeNotation::getErrorMessage()
245
    */
246
    public function getErrorCode() : int
247
    {
248
        return $this->errorCode;
249
    }
250
    
251
    protected function convert() : void
252
    {
253
        $this->parseSize();
254
        
255
        if(!$this->valid) {
256
            return;
257
        }
258
        
259
        $int = intval($this->number);
260
        
261
        // negative values
262
        if($int < 0) 
263
        {
264
            $this->setError(
265
                t('Negative values cannot be used as size.'),
266
                self::VALIDATION_ERROR_NEGATIVE_VALUE
267
            );
268
            
269
            return;
270
        }
271
        
272
        $this->size = ConvertHelper_StorageSizeEnum::getSizeByName($this->units);
0 ignored issues
show
Documentation Bug introduced by
It seems like AppUtils\ConvertHelper_S...izeByName($this->units) of type AppUtils\ConvertHelper_StorageSizeEnum_Size is incompatible with the declared type string of property $size.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
273
        
274
        $this->bytes = $int * $this->size->getBytes();
275
    }
276
}
277