Passed
Push — master ( 42ef30...517487 )
by Wilmer
09:11 queued 06:40
created

RequirementsChecker::getByteSize()   B

Complexity

Conditions 10
Paths 10

Size

Total Lines 25
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 10.3375

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 10
eloc 20
c 1
b 0
f 0
nc 10
nop 1
dl 0
loc 25
ccs 17
cts 20
cp 0.85
crap 10.3375
rs 7.6666

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
if (version_compare(PHP_VERSION, '4.3', '<')) {
4
    echo 'At least PHP 4.3 is required to run this script!';
5
    exit(1);
6
}
7
8
/**
9
 * YiiRequirementChecker allows checking, if current system meets the requirements for running the Yii application.
10
 * This class allows rendering of the check report for the web and console application interface.
11
 *
12
 * Example:
13
 *
14
 * ```php
15
 * require_once 'path/to/YiiRequirementChecker.php';
16
 * $requirementsChecker = new YiiRequirementChecker();
17
 * $requirements = array(
18
 *     array(
19
 *         'name' => 'PHP Some Extension',
20
 *         'mandatory' => true`,
21
 *         'condition' => extension_loaded('some_extension'),
22
 *         'by' => 'Some application feature',
23
 *         'memo' => 'PHP extension "some_extension" required',
24
 *     ),
25
 * );
26
 * $requirementsChecker->checkYii()->check($requirements)->render();
27
 * ```
28
 *
29
 * If you wish to render the report with your own representation, use [[getResult()]] instead of [[render()]]
30
 *
31
 * Requirement condition could be in format "eval:PHP expression".
32
 * In this case specified PHP expression will be evaluated in the context of this class instance.
33
 * For example:
34
 *
35
 * ```php
36
 * $requirements = array(
37
 *     array(
38
 *         'name' => 'Upload max file size',
39
 *         'condition' => 'eval:$this->checkUploadMaxFileSize("5M")',
40
 *     ),
41
 * );
42
 * ```
43
 *
44
 * Note: this class definition does not match ordinary Yii style, because it should match PHP 4.3
45
 * and should not use features from newer PHP versions!
46
 *
47
 * @property array|null $result the check results, this property is for internal usage only.
48
 */
49
class RequirementsChecker
50
{
51
    /**
52
     * Check the given requirements, collecting results into internal field.
53
     * This method can be invoked several times checking different requirement sets.
54
     * Use [[getResult()]] or [[render()]] to get the results.
55
     * @param array|string $requirements requirements to be checked.
56
     * If an array, it is treated as the set of requirements;
57
     * If a string, it is treated as the path of the file, which contains the requirements;
58
     * @return $this self instance.
59
     */
60 3
    public function check($requirements)
61
    {
62 3
        if (is_string($requirements)) {
63
            $requirements = require $requirements;
64
        }
65 3
        if (!is_array($requirements)) {
66
            $this->usageError('Requirements must be an array, "' . gettype($requirements) . '" has been given!');
67
        }
68 3
        if (!isset($this->result) || !is_array($this->result)) {
69 3
            $this->result = array(
70
                'summary' => array(
71
                    'total' => 0,
72
                    'errors' => 0,
73
                    'warnings' => 0,
74
                ),
75
                'requirements' => array(),
76
            );
77
        }
78 3
        foreach ($requirements as $key => $rawRequirement) {
79 3
            $requirement = $this->normalizeRequirement($rawRequirement, $key);
80 3
            $this->result['summary']['total']++;
81 3
            if (!$requirement['condition']) {
82 2
                if ($requirement['mandatory']) {
83 2
                    $requirement['error'] = true;
84 2
                    $requirement['warning'] = true;
85 2
                    $this->result['summary']['errors']++;
86
                } else {
87 1
                    $requirement['error'] = false;
88 1
                    $requirement['warning'] = true;
89 2
                    $this->result['summary']['warnings']++;
90
                }
91
            } else {
92 3
                $requirement['error'] = false;
93 3
                $requirement['warning'] = false;
94
            }
95 3
            $this->result['requirements'][] = $requirement;
96
        }
97
98 3
        return $this;
99
    }
100
101
    /**
102
     * Performs the check for the Yii core requirements.
103
     * @return RequirementsChecker self instance.
104
     */
105
    public function checkYii()
106
    {
107
        return $this->check(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'requirements.php');
108
    }
109
110
    /**
111
     * Return the check results.
112
     * @return array|null check results in format:
113
     *
114
     * ```php
115
     * array(
116
     *     'summary' => array(
117
     *         'total' => total number of checks,
118
     *         'errors' => number of errors,
119
     *         'warnings' => number of warnings,
120
     *     ),
121
     *     'requirements' => array(
122
     *         array(
123
     *             ...
124
     *             'error' => is there an error,
125
     *             'warning' => is there a warning,
126
     *         ),
127
     *         ...
128
     *     ),
129
     * )
130
     * ```
131
     */
132 3
    public function getResult()
133
    {
134 3
        if (isset($this->result)) {
135 3
            return $this->result;
136
        } else {
137
            return null;
138
        }
139
    }
140
141
    /**
142
     * Renders the requirements check result.
143
     * The output will vary depending is a script running from web or from console.
144
     */
145
    public function render()
146
    {
147
        if (!isset($this->result)) {
148
            $this->usageError('Nothing to render!');
149
        }
150
        $baseViewFilePath = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'views';
151
        if (!empty($_SERVER['argv'])) {
152
            $viewFileName = $baseViewFilePath . DIRECTORY_SEPARATOR . 'console' . DIRECTORY_SEPARATOR . 'index.php';
153
        } else {
154
            $viewFileName = $baseViewFilePath . DIRECTORY_SEPARATOR . 'web' . DIRECTORY_SEPARATOR . 'index.php';
155
        }
156
        $this->renderViewFile($viewFileName, $this->result);
157
    }
158
159
    /**
160
     * Checks if the given PHP extension is available and its version matches the given one.
161
     * @param string $extensionName PHP extension name.
162
     * @param string $version required PHP extension version.
163
     * @param string $compare comparison operator, by default '>='
164
     * @return bool if PHP extension version matches.
165
     */
166 1
    public function checkPhpExtensionVersion($extensionName, $version, $compare = '>=')
167
    {
168 1
        if (!extension_loaded($extensionName)) {
169 1
            return false;
170
        }
171 1
        $extensionVersion = phpversion($extensionName);
172 1
        if (empty($extensionVersion)) {
173
            return false;
174
        }
175 1
        if (strncasecmp($extensionVersion, 'PECL-', 5) === 0) {
176
            $extensionVersion = substr($extensionVersion, 5);
177
        }
178
179 1
        return version_compare($extensionVersion, $version, $compare);
180
    }
181
182
    /**
183
     * Checks if PHP configuration option (from php.ini) is on.
184
     * @param string $name configuration option name.
185
     * @return bool option is on.
186
     */
187
    public function checkPhpIniOn($name)
188
    {
189
        $value = ini_get($name);
190
        if (empty($value)) {
191
            return false;
192
        }
193
194
        return ((int) $value === 1 || strtolower($value) === 'on');
195
    }
196
197
    /**
198
     * Checks if PHP configuration option (from php.ini) is off.
199
     * @param string $name configuration option name.
200
     * @return bool option is off.
201
     */
202
    public function checkPhpIniOff($name)
203
    {
204
        $value = ini_get($name);
205
        if (empty($value)) {
206
            return true;
207
        }
208
209
        return (strtolower($value) === 'off');
210
    }
211
212
    /**
213
     * Compare byte sizes of values given in the verbose representation,
214
     * like '5M', '15K' etc.
215
     * @param string $a first value.
216
     * @param string $b second value.
217
     * @param string $compare comparison operator, by default '>='.
218
     * @return bool comparison result.
219
     */
220 5
    public function compareByteSize($a, $b, $compare = '>=')
221
    {
222 5
        $compareExpression = '(' . $this->getByteSize($a) . $compare . $this->getByteSize($b) . ')';
223
224 5
        return $this->evaluateExpression($compareExpression);
225
    }
226
227
    /**
228
     * Gets the size in bytes from verbose size representation.
229
     * For example: '5K' => 5*1024
230
     * @param string $verboseSize verbose size representation.
231
     * @return int actual size in bytes.
232
     */
233 12
    public function getByteSize($verboseSize)
234
    {
235 12
        if (empty($verboseSize)) {
236
            return 0;
237
        }
238 12
        if (is_numeric($verboseSize)) {
239 2
            return (int) $verboseSize;
240
        }
241 11
        $sizeUnit = trim($verboseSize, '0123456789');
242 11
        $size = trim(str_replace($sizeUnit, '', $verboseSize));
243 11
        if (!is_numeric($size)) {
244
            return 0;
245
        }
246 11
        switch (strtolower($sizeUnit)) {
247 11
            case 'kb':
248 10
            case 'k':
249 5
                return $size * 1024;
250 8
            case 'mb':
251 7
            case 'm':
252 6
                return $size * 1024 * 1024;
253 2
            case 'gb':
254 1
            case 'g':
255 2
                return $size * 1024 * 1024 * 1024;
256
            default:
257
                return 0;
258
        }
259
    }
260
261
    /**
262
     * Checks if upload max file size matches the given range.
263
     * @param string|null $min verbose file size minimum required value, pass null to skip minimum check.
264
     * @param string|null $max verbose file size maximum required value, pass null to skip maximum check.
265
     * @return bool success.
266
     */
267
    public function checkUploadMaxFileSize($min = null, $max = null)
268
    {
269
        $postMaxSize = ini_get('post_max_size');
270
        $uploadMaxFileSize = ini_get('upload_max_filesize');
271
        if ($min !== null) {
272
            $minCheckResult = $this->compareByteSize($postMaxSize, $min, '>=') && $this->compareByteSize($uploadMaxFileSize, $min, '>=');
273
        } else {
274
            $minCheckResult = true;
275
        }
276
        if ($max !== null) {
277
            $maxCheckResult = $this->compareByteSize($postMaxSize, $max, '<=') && $this->compareByteSize($uploadMaxFileSize, $max, '<=');
278
        } else {
279
            $maxCheckResult = true;
280
        }
281
282
        return ($minCheckResult && $maxCheckResult);
283
    }
284
285
    /**
286
     * Renders a view file.
287
     * This method includes the view file as a PHP script
288
     * and captures the display result if required.
289
     * @param string $_viewFile_ view file
290
     * @param array $_data_ data to be extracted and made available to the view file
291
     * @param bool $_return_ whether the rendering result should be returned as a string
292
     * @return string the rendering result. Null if the rendering result is not required.
293
     */
294
    public function renderViewFile($_viewFile_, $_data_ = null, $_return_ = false)
295
    {
296
        // we use special variable names here to avoid conflict when extracting data
297
        if (is_array($_data_)) {
298
            extract($_data_, EXTR_PREFIX_SAME, 'data');
299
        } else {
300
            $data = $_data_;
301
        }
302
        if ($_return_) {
303
            ob_start();
304
            PHP_VERSION_ID >= 80000 ? ob_implicit_flush(false) : ob_implicit_flush(0);
0 ignored issues
show
Bug introduced by
false of type false is incompatible with the type integer expected by parameter $flag of ob_implicit_flush(). ( Ignorable by Annotation )

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

304
            PHP_VERSION_ID >= 80000 ? ob_implicit_flush(/** @scrutinizer ignore-type */ false) : ob_implicit_flush(0);
Loading history...
305
            require $_viewFile_;
306
307
            return ob_get_clean();
308
        } else {
309
            require $_viewFile_;
310
        }
311
    }
312
313
    /**
314
     * Normalizes requirement ensuring it has correct format.
315
     * @param array $requirement raw requirement.
316
     * @param int $requirementKey requirement key in the list.
317
     * @return array normalized requirement.
318
     */
319 3
    public function normalizeRequirement($requirement, $requirementKey = 0)
320
    {
321 3
        if (!is_array($requirement)) {
0 ignored issues
show
introduced by
The condition is_array($requirement) is always true.
Loading history...
322
            $this->usageError('Requirement must be an array!');
323
        }
324 3
        if (!array_key_exists('condition', $requirement)) {
325
            $this->usageError("Requirement '{$requirementKey}' has no condition!");
326
        } else {
327 3
            $evalPrefix = 'eval:';
328 3
            if (is_string($requirement['condition']) && strpos($requirement['condition'], $evalPrefix) === 0) {
329 1
                $expression = substr($requirement['condition'], strlen($evalPrefix));
330 1
                $requirement['condition'] = $this->evaluateExpression($expression);
331
            }
332
        }
333 3
        if (!array_key_exists('name', $requirement)) {
334
            $requirement['name'] = is_numeric($requirementKey) ? 'Requirement #' . $requirementKey : $requirementKey;
0 ignored issues
show
introduced by
The condition is_numeric($requirementKey) is always true.
Loading history...
335
        }
336 3
        if (!array_key_exists('mandatory', $requirement)) {
337
            if (array_key_exists('required', $requirement)) {
338
                $requirement['mandatory'] = $requirement['required'];
339
            } else {
340
                $requirement['mandatory'] = false;
341
            }
342
        }
343 3
        if (!array_key_exists('by', $requirement)) {
344
            $requirement['by'] = 'Unknown';
345
        }
346 3
        if (!array_key_exists('memo', $requirement)) {
347
            $requirement['memo'] = '';
348
        }
349
350 3
        return $requirement;
351
    }
352
353
    /**
354
     * Displays a usage error.
355
     * This method will then terminate the execution of the current application.
356
     * @param string $message the error message
357
     */
358
    public function usageError($message)
359
    {
360
        echo "Error: $message\n\n";
361
        exit(1);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
362
    }
363
364
    /**
365
     * Evaluates a PHP expression under the context of this class.
366
     * @param string $expression a PHP expression to be evaluated.
367
     * @return mixed the expression result.
368
     */
369 6
    public function evaluateExpression($expression)
370
    {
371 6
        return eval('return ' . $expression . ';');
0 ignored issues
show
introduced by
The use of eval() is discouraged.
Loading history...
372
    }
373
374
    /**
375
     * Returns the server information.
376
     * @return string server information.
377
     */
378
    public function getServerInfo()
379
    {
380
        return isset($_SERVER['SERVER_SOFTWARE']) ? $_SERVER['SERVER_SOFTWARE'] : '';
381
    }
382
383
    /**
384
     * Returns the now date if possible in string representation.
385
     * @return string now date.
386
     */
387
    public function getNowDate()
388
    {
389
        return @strftime('%Y-%m-%d %H:%M', time());
390
    }
391
}
392