Passed
Push — master ( 9dbdd9...d5a428 )
by Alexander
04:15
created

framework/requirements/YiiRequirementChecker.php (1 issue)

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)
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()
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()
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()
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 = '>=')
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)
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)
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 = '>=')
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)
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)
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)
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);
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)
328
    {
329 3
        if (!is_array($requirement)) {
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;
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)
367
    {
368
        echo "Error: $message\n\n";
369
        exit(1);
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)
378
    {
379 6
        return eval('return ' . $expression . ';');
380
    }
381
382
    /**
383
     * Returns the server information.
384
     * @return string server information.
385
     */
386
    function getServerInfo()
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()
396
    {
397
        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...
398
    }
399
}
400