Test Failed
Push — master ( 489f33...4a79ed )
by Sebastian
02:17
created

ConvertHelper_SizeNotation::getErrorCode()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 3
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 $sizeString;
38
39
    /**
40
     * @var ConvertHelper_StorageSizeEnum_Size
41
     */
42
    protected $sizeDefinition;
43
    
44
   /**
45
    * @var integer
46
    */
47
    protected $bytes = 0;
48
    
49
   /**
50
    * @var bool
51
    */
52
    protected $valid = true;
53
    
54
   /**
55
    * @var string
56
    */
57
    protected $units = null;
58
    
59
   /**
60
    * @var string
61
    */
62
    protected $number = '';
63
    
64
   /**
65
    * @var string
66
    */
67
    protected $errorMessage = '';
68
    
69
   /**
70
    * @var int
71
    */
72
    protected $errorCode = 0;
73
    
74
   /**
75
    * Create a new instance for the specified size string.
76
    * 
77
    * @param string $sizeString 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". 
78
    */
79
    public function __construct(string $sizeString)
80
    {
81
        $this->sizeString = $this->cleanString($sizeString);
82
        
83
        $this->convert();
84
    }
85
    
86
   /**
87
    * Gets the amount of bytes contained in the size notation.
88
    * @return int
89
    */
90
    public function toBytes() : int
91
    {
92
        return $this->bytes;
93
    }
94
    
95
   /**
96
    * Converts the size notation to a human readable string, e.g. "50 MB".
97
    * 
98
    * @param int $precision
99
    * @return string
100
    * @see ConvertHelper::bytes2readable()
101
    */
102
    public function toString(int $precision=1) : string
103
    {
104
        return ConvertHelper::bytes2readable($this->bytes, $precision, $this->getBase());
105
    }
106
    
107
   /**
108
    * Retrieves the detected number's base.
109
    * @return int The base number (1000 for Base 10, or 1024 for Base 2), or 0 if it is not valid.
110
    */
111
    public function getBase() : int
112
    {
113
        if($this->isValid()) {
114
            return $this->sizeDefinition->getBase(); 
115
        }
116
        
117
        return 0;
118
    }
119
    
120
   /**
121
    * Retrieves the detected storage size instance, if 
122
    * the size string is valid.
123
    * 
124
    * @return ConvertHelper_StorageSizeEnum_Size|NULL
125
    * @see ConvertHelper_StorageSizeEnum_Size
126
    */
127
    public function getSizeDefinition() : ?ConvertHelper_StorageSizeEnum_Size
128
    {
129
        if($this->isValid()) {
130
            return $this->sizeDefinition;
131
        }
132
        
133
        return null;
134
    }
135
    
136
   /**
137
    * Checks whether the size notation was valid and could be parsed
138
    * into a meaningful byte value. If this returns `false`, it is 
139
    * possible to use the `getErrorXXX` methods to retrieve information
140
    * on what went wrong. 
141
    * 
142
    * @return bool
143
    * @see ConvertHelper_SizeNotation::getErrorMessage()
144
    * @see ConvertHelper_SizeNotation::getErrorCode()
145
    */
146
    public function isValid() : bool
147
    {
148
        return $this->valid;
149
    }
150
    
151
   /**
152
    * Retrieves the error message if the size notation validation failed.
153
    * 
154
    * @return string
155
    * @see ConvertHelper_SizeNotation::getErrorCode()
156
    */
157
    public function getErrorMessage() : string
158
    {
159
        return $this->errorMessage;
160
    }
161
    
162
    protected function cleanString(string $string) : string
163
    {
164
        // remove spaces
165
        $result = trim(str_replace(' ', '', $string));
166
        
167
        // convert numeric notation with commas instead of dots
168
        $result = str_replace(',', '.', $result);
169
        
170
        // for case insensitivity, treat it all lowercase
171
        $result = strtolower($result);
172
        
173
        return $result;
174
    }
175
    
176
    protected function parseSize() : void
177
    {
178
        if(!$this->detectParts()) {
179
            return;
180
        }
181
        
182
        // we detected units in the string: all good.
183
        if($this->units !== null)
184
        {
185
            return;
186
        }
187
        
188
        // just a numeric value: we assume this means we're dealing with bytes.
189
        if(is_numeric($this->number)) 
190
        {
191
            $this->units = 'b';
192
            return;
193
        } 
194
        
195
        // no units found: this can be either a raw byte value,
196
        // or some other meaningless string.
197
        $this->setError(
198
            t('Unrecognized size string.'),
199
            self::VALIDATION_ERROR_UNRECOGNIZED_STRING
200
        );
201
    }
202
    
203
   /**
204
    * Detects the units and the number in the size notation string.
205
    * Populates the `units` and `number` properties.
206
    * 
207
    * @return bool Whether the string could be parsed successfully.
208
    */
209
    protected function detectParts() : bool
210
    {
211
        $units = ConvertHelper_StorageSizeEnum::getSizeNames();
212
        
213
        $number = $this->sizeString;
214
        
215
        foreach($units as $unit)
216
        {
217
            if(stristr($number, $unit))
218
            {
219
                // there are more than 1 unit defined in the string
220
                if($this->units !== null)
221
                {
222
                    $this->setError(
223
                        t(
224
                            'Multiple units defined in the string (%1$s and %2$s).',
225
                            $this->units,
226
                            $unit
227
                        ),
228
                        self::VALIDATION_ERROR_MULTIPLE_UNITS
229
                    );
230
                    
231
                    return false;
232
                }
233
                
234
                $this->units = $unit;
235
                $number = str_replace($unit, '', $number);
236
            }
237
        }
238
        
239
        $this->number = $number;
240
        
241
        return true;
242
    }
243
    
244
   /**
245
    * If the validation fails, this is used to store the reason for retrieval later.
246
    *  
247
    * @param string $message
248
    * @param int $code
249
    * 
250
    * @see ConvertHelper_SizeNotation::isValid()
251
    * @see ConvertHelper_SizeNotation::getErrorMessage()
252
    * @see ConvertHelper_SizeNotation::getErrorCode()
253
    */
254
    protected function setError(string $message, int $code) : void
255
    {
256
        $this->valid = false;
257
        $this->errorMessage = $message;
258
        $this->errorCode = $code;
259
    }
260
    
261
   /**
262
    * Retrieves the error code, if the size notation validation failed.
263
    * 
264
    * @return int
265
    * @see ConvertHelper_SizeNotation::getErrorMessage()
266
    */
267
    public function getErrorCode() : int
268
    {
269
        return $this->errorCode;
270
    }
271
    
272
    protected function convert() : void
273
    {
274
        $this->parseSize();
275
        
276
        if(!$this->valid) {
277
            return;
278
        }
279
        
280
        $int = intval($this->number);
281
        
282
        // negative values
283
        if($int < 0) 
284
        {
285
            $this->setError(
286
                t('Negative values cannot be used as size.'),
287
                self::VALIDATION_ERROR_NEGATIVE_VALUE
288
            );
289
            
290
            return;
291
        }
292
        
293
        $this->sizeDefinition = ConvertHelper_StorageSizeEnum::getSizeByName($this->units);
294
        
295
        $this->bytes = $int * $this->sizeString->getBytes();
296
    }
297
}
298