Issues (836)

framework/requirements/YiiRequirementChecker.php (22 issues)

1
<?php
2
/**
3
 * @link https://www.yiiframework.com/
4
 * @copyright Copyright (c) 2008 Yii Software LLC
5
 * @license https://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
     * @var Check result
0 ignored issues
show
The type Check was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
61
     */
62
    public $result;
63
64
    /**
65
     * Check the given requirements, collecting results into internal field.
66
     * This method can be invoked several times checking different requirement sets.
67
     * Use [[getResult()]] or [[render()]] to get the results.
68
     * @param array|string $requirements requirements to be checked.
69
     * If an array, it is treated as the set of requirements;
70
     * If a string, it is treated as the path of the file, which contains the requirements;
71
     * @return $this self instance.
72
     */
73
    function check($requirements)
74
    {
75
        if (is_string($requirements)) {
76
            $requirements = require $requirements;
77
        }
78
        if (!is_array($requirements)) {
79
            $this->usageError('Requirements must be an array, "' . gettype($requirements) . '" has been given!');
80
        }
81
        if (!isset($this->result) || !is_array($this->result)) {
82
            $this->result = array(
83
                'summary' => array(
84
                    'total' => 0,
85
                    'errors' => 0,
86
                    'warnings' => 0,
87
                ),
88
                'requirements' => array(),
89
            );
90
        }
91
        foreach ($requirements as $key => $rawRequirement) {
92
            $requirement = $this->normalizeRequirement($rawRequirement, $key);
93
            $this->result['summary']['total']++;
94
            if (!$requirement['condition']) {
95
                if ($requirement['mandatory']) {
96
                    $requirement['error'] = true;
97
                    $requirement['warning'] = true;
98
                    $this->result['summary']['errors']++;
99
                } else {
100
                    $requirement['error'] = false;
101
                    $requirement['warning'] = true;
102
                    $this->result['summary']['warnings']++;
103
                }
104
            } else {
105
                $requirement['error'] = false;
106
                $requirement['warning'] = false;
107
            }
108
            $this->result['requirements'][] = $requirement;
109
        }
110
111
        return $this;
112
    }
113
114
    /**
115
     * Performs the check for the Yii core requirements.
116
     * @return YiiRequirementChecker self instance.
117
     */
118
    function checkYii()
0 ignored issues
show
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...
119
    {
120
        return $this->check(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'requirements.php');
121
    }
122
123
    /**
124
     * Return the check results.
125
     * @return array|null check results in format:
126
     *
127
     * ```php
128
     * array(
129
     *     'summary' => array(
130
     *         'total' => total number of checks,
131
     *         'errors' => number of errors,
132
     *         'warnings' => number of warnings,
133
     *     ),
134
     *     'requirements' => array(
135
     *         array(
136
     *             ...
137
     *             'error' => is there an error,
138
     *             'warning' => is there a warning,
139
     *         ),
140
     *         ...
141
     *     ),
142
     * )
143
     * ```
144
     */
145
    function getResult()
0 ignored issues
show
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...
146
    {
147
        if (isset($this->result)) {
148
            return $this->result;
149
        } else {
150
            return null;
151
        }
152
    }
153
154
    /**
155
     * Renders the requirements check result.
156
     * The output will vary depending is a script running from web or from console.
157
     */
158
    function render()
0 ignored issues
show
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...
159
    {
160
        if (!isset($this->result)) {
161
            $this->usageError('Nothing to render!');
162
        }
163
        $baseViewFilePath = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'views';
164
        if (!empty($_SERVER['argv'])) {
165
            $viewFileName = $baseViewFilePath . DIRECTORY_SEPARATOR . 'console' . DIRECTORY_SEPARATOR . 'index.php';
166
        } else {
167
            $viewFileName = $baseViewFilePath . DIRECTORY_SEPARATOR . 'web' . DIRECTORY_SEPARATOR . 'index.php';
168
        }
169
        $this->renderViewFile($viewFileName, $this->result);
170
    }
171
172
    /**
173
     * Checks if the given PHP extension is available and its version matches the given one.
174
     * @param string $extensionName PHP extension name.
175
     * @param string $version required PHP extension version.
176
     * @param string $compare comparison operator, by default '>='
177
     * @return bool if PHP extension version matches.
178
     */
179
    function checkPhpExtensionVersion($extensionName, $version, $compare = '>=')
0 ignored issues
show
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...
180
    {
181
        if (!extension_loaded($extensionName)) {
182
            return false;
183
        }
184
        $extensionVersion = phpversion($extensionName);
185
        if (empty($extensionVersion)) {
186
            return false;
187
        }
188
        if (strncasecmp($extensionVersion, 'PECL-', 5) === 0) {
189
            $extensionVersion = substr($extensionVersion, 5);
190
        }
191
192
        return version_compare($extensionVersion, $version, $compare);
0 ignored issues
show
Bug Best Practice introduced by
The expression return version_compare($...on, $version, $compare) also could return the type integer which is incompatible with the documented return type boolean.
Loading history...
193
    }
194
195
    /**
196
     * Checks if PHP configuration option (from php.ini) is on.
197
     * @param string $name configuration option name.
198
     * @return bool option is on.
199
     */
200
    function checkPhpIniOn($name)
0 ignored issues
show
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...
201
    {
202
        $value = ini_get($name);
203
        if (empty($value)) {
204
            return false;
205
        }
206
207
        return ((int) $value === 1 || strtolower($value) === 'on');
208
    }
209
210
    /**
211
     * Checks if PHP configuration option (from php.ini) is off.
212
     * @param string $name configuration option name.
213
     * @return bool option is off.
214
     */
215
    function checkPhpIniOff($name)
0 ignored issues
show
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...
216
    {
217
        $value = ini_get($name);
218
        if (empty($value)) {
219
            return true;
220
        }
221
222
        return (strtolower($value) === 'off');
223
    }
224
225
    /**
226
     * Compare byte sizes of values given in the verbose representation,
227
     * like '5M', '15K' etc.
228
     * @param string $a first value.
229
     * @param string $b second value.
230
     * @param string $compare comparison operator, by default '>='.
231
     * @return bool comparison result.
232
     */
233
    function compareByteSize($a, $b, $compare = '>=')
0 ignored issues
show
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...
234
    {
235
        $compareExpression = '(' . $this->getByteSize($a) . $compare . $this->getByteSize($b) . ')';
236
237
        return $this->evaluateExpression($compareExpression);
238
    }
239
240
    /**
241
     * Gets the size in bytes from verbose size representation.
242
     * For example: '5K' => 5*1024
243
     * @param string $verboseSize verbose size representation.
244
     * @return int actual size in bytes.
245
     */
246
    function getByteSize($verboseSize)
0 ignored issues
show
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...
247
    {
248
        if (empty($verboseSize)) {
249
            return 0;
250
        }
251
        if (is_numeric($verboseSize)) {
252
            return (int) $verboseSize;
253
        }
254
        $sizeUnit = trim($verboseSize, '0123456789');
255
        $size = trim(str_replace($sizeUnit, '', $verboseSize));
256
        if (!is_numeric($size)) {
257
            return 0;
258
        }
259
        switch (strtolower($sizeUnit)) {
260
            case 'kb':
261
            case 'k':
262
                return $size * 1024;
263
            case 'mb':
264
            case 'm':
265
                return $size * 1024 * 1024;
266
            case 'gb':
267
            case 'g':
268
                return $size * 1024 * 1024 * 1024;
269
            default:
270
                return 0;
271
        }
272
    }
273
274
    /**
275
     * Checks if upload max file size matches the given range.
276
     * @param string|null $min verbose file size minimum required value, pass null to skip minimum check.
277
     * @param string|null $max verbose file size maximum required value, pass null to skip maximum check.
278
     * @return bool success.
279
     */
280
    function checkUploadMaxFileSize($min = null, $max = null)
0 ignored issues
show
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...
281
    {
282
        $postMaxSize = ini_get('post_max_size');
283
        $uploadMaxFileSize = ini_get('upload_max_filesize');
284
        if ($min !== null) {
285
            $minCheckResult = $this->compareByteSize($postMaxSize, $min, '>=') && $this->compareByteSize($uploadMaxFileSize, $min, '>=');
286
        } else {
287
            $minCheckResult = true;
288
        }
289
        if ($max !== null) {
290
            $maxCheckResult = $this->compareByteSize($postMaxSize, $max, '<=') && $this->compareByteSize($uploadMaxFileSize, $max, '<=');
291
        } else {
292
            $maxCheckResult = true;
293
        }
294
295
        return ($minCheckResult && $maxCheckResult);
296
    }
297
298
    /**
299
     * Renders a view file.
300
     * This method includes the view file as a PHP script
301
     * and captures the display result if required.
302
     * @param string $_viewFile_ view file
303
     * @param array|null $_data_ data to be extracted and made available to the view file
304
     * @param bool $_return_ whether the rendering result should be returned as a string
305
     * @return string|null the rendering result. Null if the rendering result is not required.
306
     */
307
    function renderViewFile($_viewFile_, $_data_ = null, $_return_ = false)
0 ignored issues
show
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...
308
    {
309
        // we use special variable names here to avoid conflict when extracting data
310
        if (is_array($_data_)) {
311
            extract($_data_, EXTR_PREFIX_SAME, 'data');
312
        } else {
313
            $data = $_data_;
314
        }
315
        if ($_return_) {
316
            ob_start();
317
            ob_implicit_flush(false);
318
            require $_viewFile_;
319
320
            return ob_get_clean();
321
        } else {
322
            require $_viewFile_;
323
        }
324
    }
325
326
    /**
327
     * Normalizes requirement ensuring it has correct format.
328
     * @param array $requirement raw requirement.
329
     * @param int $requirementKey requirement key in the list.
330
     * @return array normalized requirement.
331
     */
332
    function normalizeRequirement($requirement, $requirementKey = 0)
0 ignored issues
show
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...
333
    {
334
        if (!is_array($requirement)) {
0 ignored issues
show
The condition is_array($requirement) is always true.
Loading history...
335
            $this->usageError('Requirement must be an array!');
336
        }
337
        if (!array_key_exists('condition', $requirement)) {
338
            $this->usageError("Requirement '{$requirementKey}' has no condition!");
339
        } else {
340
            $evalPrefix = 'eval:';
341
            if (is_string($requirement['condition']) && strpos($requirement['condition'], $evalPrefix) === 0) {
342
                $expression = substr($requirement['condition'], strlen($evalPrefix));
343
                $requirement['condition'] = $this->evaluateExpression($expression);
344
            }
345
        }
346
        if (!array_key_exists('name', $requirement)) {
347
            $requirement['name'] = is_numeric($requirementKey) ? 'Requirement #' . $requirementKey : $requirementKey;
0 ignored issues
show
The condition is_numeric($requirementKey) is always true.
Loading history...
348
        }
349
        if (!array_key_exists('mandatory', $requirement)) {
350
            if (array_key_exists('required', $requirement)) {
351
                $requirement['mandatory'] = $requirement['required'];
352
            } else {
353
                $requirement['mandatory'] = false;
354
            }
355
        }
356
        if (!array_key_exists('by', $requirement)) {
357
            $requirement['by'] = 'Unknown';
358
        }
359
        if (!array_key_exists('memo', $requirement)) {
360
            $requirement['memo'] = '';
361
        }
362
363
        return $requirement;
364
    }
365
366
    /**
367
     * Displays a usage error.
368
     * This method will then terminate the execution of the current application.
369
     * @param string $message the error message
370
     */
371
    function usageError($message)
0 ignored issues
show
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...
372
    {
373
        echo "Error: $message\n\n";
374
        exit(1);
0 ignored issues
show
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...
375
    }
376
377
    /**
378
     * Evaluates a PHP expression under the context of this class.
379
     * @param string $expression a PHP expression to be evaluated.
380
     * @return mixed the expression result.
381
     */
382
    function evaluateExpression($expression)
0 ignored issues
show
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...
383
    {
384
        return eval('return ' . $expression . ';');
0 ignored issues
show
The use of eval() is discouraged.
Loading history...
385
    }
386
387
    /**
388
     * Returns the server information.
389
     * @return string server information.
390
     */
391
    function getServerInfo()
0 ignored issues
show
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...
392
    {
393
        return isset($_SERVER['SERVER_SOFTWARE']) ? $_SERVER['SERVER_SOFTWARE'] : '';
394
    }
395
396
    /**
397
     * Returns the now date if possible in string representation.
398
     * @return string now date.
399
     */
400
    function getNowDate()
0 ignored issues
show
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...
401
    {
402
        return @strftime('%Y-%m-%d %H:%M', time());
0 ignored issues
show
Bug Best Practice introduced by
The expression return @strftime('%Y-%m-%d %H:%M', time()) could also return false which is incompatible with the documented return type string. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
403
    }
404
}
405