Passed
Push — scrutinizer-migrate-to-new-eng... ( 58afd6 )
by Alexander
18:11
created

YiiRequirementChecker   D

Complexity

Total Complexity 59

Size/Duplication

Total Lines 341
Duplicated Lines 0 %

Test Coverage

Coverage 49.61%

Importance

Changes 0
Metric Value
eloc 128
dl 0
loc 341
ccs 64
cts 129
cp 0.4961
rs 4.08
c 0
b 0
f 0
wmc 59

16 Methods

Rating   Name   Duplication   Size   Complexity  
A renderViewFile() 0 16 3
A checkUploadMaxFileSize() 0 16 6
A evaluateExpression() 0 3 1
A checkPhpIniOn() 0 8 3
A getResult() 0 6 2
A checkYii() 0 3 1
C normalizeRequirement() 0 32 11
A getNowDate() 0 3 1
A checkPhpExtensionVersion() 0 14 4
A render() 0 12 3
B getByteSize() 0 25 10
B check() 0 39 8
A checkPhpIniOff() 0 8 2
A usageError() 0 4 1
A getServerInfo() 0 3 2
A compareByteSize() 0 5 1

How to fix   Complexity   

Complex Class

Complex classes like YiiRequirementChecker 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.

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 YiiRequirementChecker, and based on these observations, apply Extract Interface, too.

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

312
            ob_implicit_flush(/** @scrutinizer ignore-type */ false);
Loading history...
313
            require $_viewFile_;
314
315
            return ob_get_clean();
316
        } else {
317
            require $_viewFile_;
318
        }
319
    }
320
321
    /**
322
     * Normalizes requirement ensuring it has correct format.
323
     * @param array $requirement raw requirement.
324
     * @param int $requirementKey requirement key in the list.
325
     * @return array normalized requirement.
326
     */
327 3
    function normalizeRequirement($requirement, $requirementKey = 0)
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...
328
    {
329 3
        if (!is_array($requirement)) {
0 ignored issues
show
introduced by
The condition is_array($requirement) is always true.
Loading history...
330
            $this->usageError('Requirement must be an array!');
331
        }
332 3
        if (!array_key_exists('condition', $requirement)) {
333
            $this->usageError("Requirement '{$requirementKey}' has no condition!");
334
        } else {
335 3
            $evalPrefix = 'eval:';
336 3
            if (is_string($requirement['condition']) && strpos($requirement['condition'], $evalPrefix) === 0) {
337 1
                $expression = substr($requirement['condition'], strlen($evalPrefix));
338 1
                $requirement['condition'] = $this->evaluateExpression($expression);
339
            }
340
        }
341 3
        if (!array_key_exists('name', $requirement)) {
342
            $requirement['name'] = is_numeric($requirementKey) ? 'Requirement #' . $requirementKey : $requirementKey;
0 ignored issues
show
introduced by
The condition is_numeric($requirementKey) is always true.
Loading history...
343
        }
344 3
        if (!array_key_exists('mandatory', $requirement)) {
345
            if (array_key_exists('required', $requirement)) {
346
                $requirement['mandatory'] = $requirement['required'];
347
            } else {
348
                $requirement['mandatory'] = false;
349
            }
350
        }
351 3
        if (!array_key_exists('by', $requirement)) {
352
            $requirement['by'] = 'Unknown';
353
        }
354 3
        if (!array_key_exists('memo', $requirement)) {
355
            $requirement['memo'] = '';
356
        }
357
358 3
        return $requirement;
359
    }
360
361
    /**
362
     * Displays a usage error.
363
     * This method will then terminate the execution of the current application.
364
     * @param string $message the error message
365
     */
366
    function usageError($message)
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...
367
    {
368
        echo "Error: $message\n\n";
369
        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...
370
    }
371
372
    /**
373
     * Evaluates a PHP expression under the context of this class.
374
     * @param string $expression a PHP expression to be evaluated.
375
     * @return mixed the expression result.
376
     */
377 6
    function evaluateExpression($expression)
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...
378
    {
379 6
        return eval('return ' . $expression . ';');
0 ignored issues
show
introduced by
The use of eval() is discouraged.
Loading history...
380
    }
381
382
    /**
383
     * Returns the server information.
384
     * @return string server information.
385
     */
386
    function getServerInfo()
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...
387
    {
388
        return isset($_SERVER['SERVER_SOFTWARE']) ? $_SERVER['SERVER_SOFTWARE'] : '';
389
    }
390
391
    /**
392
     * Returns the now date if possible in string representation.
393
     * @return string now date.
394
     */
395
    function getNowDate()
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...
396
    {
397
        return @strftime('%Y-%m-%d %H:%M', time());
398
    }
399
}
400