Completed
Push — master ( 4a3e00...b18edd )
by Oleg
10:47
created

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
// @codingStandardsIgnoreFile
3
// @codeCoverageIgnoreStart
4
5
/**
6
 * C3 - Codeception Code Coverage
7
 *
8
 * @author tiger
9
 */
10
11
// $_SERVER['HTTP_X_CODECEPTION_CODECOVERAGE_DEBUG'] = 1;
12
13
if (isset($_COOKIE['CODECEPTION_CODECOVERAGE'])) {
14
    $cookie = json_decode($_COOKIE['CODECEPTION_CODECOVERAGE'], true);
15
16
    // fix for improperly encoded JSON in Code Coverage cookie with WebDriver.
17
    // @see https://github.com/Codeception/Codeception/issues/874
18
    if (!is_array($cookie)) {
19
        $cookie = json_decode($cookie, true);
20
    }
21
22
    if ($cookie) {
23
        foreach ($cookie as $key => $value) {
24
            $_SERVER["HTTP_X_CODECEPTION_" . strtoupper($key)] = $value;
25
        }
26
    }
27
}
28
29
if (!array_key_exists('HTTP_X_CODECEPTION_CODECOVERAGE', $_SERVER)) {
30
    return;
31
}
32
33
if (!function_exists('__c3_error')) {
34
    function __c3_error($message)
35
    {
36
        $errorLogFile = defined('C3_CODECOVERAGE_ERROR_LOG_FILE') ?
37
            C3_CODECOVERAGE_ERROR_LOG_FILE :
38
            C3_CODECOVERAGE_MEDIATE_STORAGE . DIRECTORY_SEPARATOR . 'error.txt';
39
        if (is_writable($errorLogFile)) {
40
            file_put_contents($errorLogFile, $message);
41
        } else {
42
            $message = "Could not write error to log file ($errorLogFile), original message: $message";
43
        }
44
        if (!headers_sent()) {
45
            header('X-Codeception-CodeCoverage-Error: ' . str_replace("\n", ' ', $message), true, 500);
46
        }
47
        setcookie('CODECEPTION_CODECOVERAGE_ERROR', $message);
48
    }
49
}
50
51
// phpunit codecoverage shimming
52
if (!class_exists('PHP_CodeCoverage') and class_exists('SebastianBergmann\CodeCoverage\CodeCoverage')) {
53
    class_alias('SebastianBergmann\CodeCoverage\CodeCoverage', 'PHP_CodeCoverage');
54
    class_alias('SebastianBergmann\CodeCoverage\Report\Text', 'PHP_CodeCoverage_Report_Text');
55
    class_alias('SebastianBergmann\CodeCoverage\Report\PHP', 'PHP_CodeCoverage_Report_PHP');
56
    class_alias('SebastianBergmann\CodeCoverage\Report\Clover', 'PHP_CodeCoverage_Report_Clover');
57
    class_alias('SebastianBergmann\CodeCoverage\Report\Crap4j', 'PHP_CodeCoverage_Report_Crap4j');
58
    class_alias('SebastianBergmann\CodeCoverage\Report\Html\Facade', 'PHP_CodeCoverage_Report_HTML');
59
    class_alias('SebastianBergmann\CodeCoverage\Report\Xml\Facade', 'PHP_CodeCoverage_Report_XML');
60
    class_alias('SebastianBergmann\CodeCoverage\Exception', 'PHP_CodeCoverage_Exception');
61
}
62
// phpunit version
63
if (!class_exists('PHPUnit_Runner_Version') && class_exists('PHPUnit\Runner\Version')) {
64
    class_alias('PHPUnit\Runner\Version', 'PHPUnit_Runner_Version');
65
}    
66
67
// Autoload Codeception classes
68
if (!class_exists('\\Codeception\\Codecept')) {
69
    if (file_exists(__DIR__ . '/codecept.phar')) {
70
        require_once 'phar://' . __DIR__ . '/codecept.phar/autoload.php';
71
    } elseif (stream_resolve_include_path(__DIR__ . '/vendor/autoload.php')) {
72
        require_once __DIR__ . '/vendor/autoload.php';
73
        // Required to load some methods only available at codeception/autoload.php
74
        if (stream_resolve_include_path(__DIR__ . '/vendor/codeception/codeception/autoload.php')) {
75
            require_once __DIR__ . '/vendor/codeception/codeception/autoload.php';
76
        }
77
    } elseif (stream_resolve_include_path('Codeception/autoload.php')) {
78
        require_once 'Codeception/autoload.php';
79
    } else {
80
        __c3_error('Codeception is not loaded. Please check that either PHAR or Composer package can be used');
81
    }
82
}
83
84
// Load Codeception Config
85
$config_dist_file = realpath(__DIR__) . DIRECTORY_SEPARATOR . 'codeception.dist.yml';
86
$config_file = realpath(__DIR__) . DIRECTORY_SEPARATOR . 'codeception.yml';
87
88
if (isset($_SERVER['HTTP_X_CODECEPTION_CODECOVERAGE_CONFIG'])) {
89
    $config_file = realpath(__DIR__) . DIRECTORY_SEPARATOR . $_SERVER['HTTP_X_CODECEPTION_CODECOVERAGE_CONFIG'];
90
}
91
if (file_exists($config_file)) {
92
    // Use codeception.yml for configuration.
93
} elseif (file_exists($config_dist_file)) {
94
    // Use codeception.dist.yml for configuration.
95
    $config_file = $config_dist_file;
96
} else {
97
    __c3_error(sprintf("Codeception config file '%s' not found", $config_file));
98
}
99
try {
100
    \Codeception\Configuration::config($config_file);
101
} catch (\Exception $e) {
102
    __c3_error($e->getMessage());
103
}
104
105
if (!defined('C3_CODECOVERAGE_MEDIATE_STORAGE')) {
106
107
    // workaround for 'zend_mm_heap corrupted' problem
108
    gc_disable();
109
110
    $memoryLimit = ini_get('memory_limit');
111
    $requiredMemory = '384M';
112
    if ((substr($memoryLimit, -1) === 'M' && (int)$memoryLimit < (int)$requiredMemory)
113
        || (substr($memoryLimit, -1) === 'K' && (int)$memoryLimit < (int)$requiredMemory * 1024)
114
        || (ctype_digit($memoryLimit) && (int)$memoryLimit < (int)$requiredMemory * 1024 * 1024)
115
    ) {
116
        ini_set('memory_limit', $requiredMemory);
117
    }
118
119
    define('C3_CODECOVERAGE_MEDIATE_STORAGE', Codeception\Configuration::logDir() . 'c3tmp');
120
    define('C3_CODECOVERAGE_PROJECT_ROOT', Codeception\Configuration::projectDir());
121
    define('C3_CODECOVERAGE_TESTNAME', $_SERVER['HTTP_X_CODECEPTION_CODECOVERAGE']);
122
123
    function __c3_build_html_report(PHP_CodeCoverage $codeCoverage, $path)
124
    {
125
        $writer = new PHP_CodeCoverage_Report_HTML();
126
        $writer->process($codeCoverage, $path . 'html');
127
128
        if (file_exists($path . '.tar')) {
129
            unlink($path . '.tar');
130
        }
131
132
        $phar = new PharData($path . '.tar');
133
        $phar->setSignatureAlgorithm(Phar::SHA1);
134
        $files = $phar->buildFromDirectory($path . 'html');
135
        array_map('unlink', $files);
136
137
        if (in_array('GZ', Phar::getSupportedCompression())) {
138
            if (file_exists($path . '.tar.gz')) {
139
                unlink($path . '.tar.gz');
140
            }
141
142
            $phar->compress(\Phar::GZ);
143
144
            // close the file so that we can rename it
145
            unset($phar);
146
147
            unlink($path . '.tar');
148
            rename($path . '.tar.gz', $path . '.tar');
149
        }
150
151
        return $path . '.tar';
152
    }
153
154
    function __c3_build_clover_report(PHP_CodeCoverage $codeCoverage, $path)
155
    {
156
        $writer = new PHP_CodeCoverage_Report_Clover();
157
        $writer->process($codeCoverage, $path . '.clover.xml');
158
159
        return $path . '.clover.xml';
160
    }
161
162
    function __c3_build_crap4j_report(PHP_CodeCoverage $codeCoverage, $path)
163
    {
164
        $writer = new PHP_CodeCoverage_Report_Crap4j();
165
        $writer->process($codeCoverage, $path . '.crap4j.xml');
166
167
        return $path . '.crap4j.xml';
168
    }
169
170
    function __c3_build_phpunit_report(PHP_CodeCoverage $codeCoverage, $path)
171
    {
172
        $writer = new PHP_CodeCoverage_Report_XML(\PHPUnit_Runner_Version::id());
173
        $writer->process($codeCoverage, $path . 'phpunit');
174
175
        if (file_exists($path . '.tar')) {
176
            unlink($path . '.tar');
177
        }
178
179
        $phar = new PharData($path . '.tar');
180
        $phar->setSignatureAlgorithm(Phar::SHA1);
181
        $files = $phar->buildFromDirectory($path . 'phpunit');
182
        array_map('unlink', $files);
183
184
        if (in_array('GZ', Phar::getSupportedCompression())) {
185
            if (file_exists($path . '.tar.gz')) {
186
                unlink($path . '.tar.gz');
187
            }
188
189
            $phar->compress(\Phar::GZ);
190
191
            // close the file so that we can rename it
192
            unset($phar);
193
194
            unlink($path . '.tar');
195
            rename($path . '.tar.gz', $path . '.tar');
196
        }
197
198
        return $path . '.tar';
199
    }
200
201
    function __c3_send_file($filename)
202
    {
203
        if (!headers_sent()) {
204
            readfile($filename);
205
        }
206
207
        return __c3_exit();
208
    }
209
210
    /**
211
     * @param $filename
212
     * @param bool $lock Lock the file for writing?
213
     * @return [null|PHP_CodeCoverage|\SebastianBergmann\CodeCoverage\CodeCoverage, resource]
0 ignored issues
show
The doc-type null|PHP_CodeCoverage|\SebastianBergmann\CodeCoverage\CodeCoverage,">[null|PHP_CodeCoverage|\...eCoverage\CodeCoverage, could not be parsed: Unknown type name "[" at position 0. [(view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
214
     */
215
    function __c3_factory($filename, $lock=false)
216
    {
217
        $file = null;
218
        if ($filename !== null && is_readable($filename)) {
219
            if ($lock) {
220
                $file = fopen($filename, 'r+');
221
                if (flock($file, LOCK_EX)) {
222
                    $phpCoverage = unserialize(stream_get_contents($file));
223
                } else {
224
                    __c3_error("Failed to acquire write-lock for $filename");
225
                }
226
            } else {
227
                $phpCoverage = unserialize(file_get_contents($filename));
228
            }
229
            
230
            return array($phpCoverage, $file);
0 ignored issues
show
The variable $phpCoverage does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
231
        } else {
232
            $phpCoverage = new PHP_CodeCoverage();
233
        }
234
235
        if (isset($_SERVER['HTTP_X_CODECEPTION_CODECOVERAGE_SUITE'])) {
236
            $suite = $_SERVER['HTTP_X_CODECEPTION_CODECOVERAGE_SUITE'];
237
            try {
238
                $settings = \Codeception\Configuration::suiteSettings($suite, \Codeception\Configuration::config());
239
            } catch (Exception $e) {
240
                __c3_error($e->getMessage());
241
            }
242
        } else {
243
            $settings = \Codeception\Configuration::config();
244
        }
245
246
        try {
247
            \Codeception\Coverage\Filter::setup($phpCoverage)
248
                ->whiteList($settings)
249
                ->blackList($settings);
250
        } catch (Exception $e) {
251
            __c3_error($e->getMessage());
252
        }
253
254
        return array($phpCoverage, $file);
255
    }
256
257
    function __c3_exit()
258
    {
259
        if (!isset($_SERVER['HTTP_X_CODECEPTION_CODECOVERAGE_DEBUG'])) {
260
            exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The function __c3_exit() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
261
        }
262
        return null;
263
    }
264
265
    function __c3_clear()
266
    {
267
        \Codeception\Util\FileSystem::doEmptyDir(C3_CODECOVERAGE_MEDIATE_STORAGE);
268
    }
269
}
270
271
if (!is_dir(C3_CODECOVERAGE_MEDIATE_STORAGE)) {
272
    if (mkdir(C3_CODECOVERAGE_MEDIATE_STORAGE, 0777, true) === false) {
273
        __c3_error('Failed to create directory "' . C3_CODECOVERAGE_MEDIATE_STORAGE . '"');
274
    }
275
}
276
277
// evaluate base path for c3-related files
278
$path = realpath(C3_CODECOVERAGE_MEDIATE_STORAGE) . DIRECTORY_SEPARATOR . 'codecoverage';
279
280
$requested_c3_report = (strpos($_SERVER['REQUEST_URI'], 'c3/report') !== false);
281
282
$complete_report = $current_report = $path . '.serialized';
283
if ($requested_c3_report) {
284
    set_time_limit(0);
285
286
    $route = ltrim(strrchr($_SERVER['REQUEST_URI'], '/'), '/');
287
288
    if ($route === 'clear') {
289
        __c3_clear();
290
        return __c3_exit();
291
    }
292
293
    list($codeCoverage, ) = __c3_factory($complete_report);
294
295
    switch ($route) {
296
        case 'html':
297
            try {
298
                __c3_send_file(__c3_build_html_report($codeCoverage, $path));
299
            } catch (Exception $e) {
300
                __c3_error($e->getMessage());
301
            }
302
            return __c3_exit();
303
        case 'clover':
304
            try {
305
                __c3_send_file(__c3_build_clover_report($codeCoverage, $path));
306
            } catch (Exception $e) {
307
                __c3_error($e->getMessage());
308
            }
309
            return __c3_exit();
310
        case 'crap4j':
311
            try {
312
                __c3_send_file(__c3_build_crap4j_report($codeCoverage, $path));
313
            } catch (Exception $e) {
314
                __c3_error($e->getMessage());
315
            }
316
            return __c3_exit();
317
        case 'serialized':
318
            try {
319
                __c3_send_file($complete_report);
320
            } catch (Exception $e) {
321
                __c3_error($e->getMessage());
322
            }
323
            return __c3_exit();
324
        case 'phpunit':
325
            try {
326
                __c3_send_file(__c3_build_phpunit_report($codeCoverage, $path));
327
            } catch (Exception $e) {
328
                __c3_error($e->getMessage());
329
            }
330
            return __c3_exit();
331
    }
332
333
} else {
334
    list($codeCoverage, ) = __c3_factory(null);
335
    $codeCoverage->start(C3_CODECOVERAGE_TESTNAME);
336
    if (!array_key_exists('HTTP_X_CODECEPTION_CODECOVERAGE_DEBUG', $_SERVER)) {
337
        register_shutdown_function(
338
            function () use ($codeCoverage, $current_report) {
339
340
                $codeCoverage->stop();
341
                if (!file_exists(dirname($current_report))) { // verify directory exists
342
                    if (!mkdir(dirname($current_report), 0777, true)) {
343
                        __c3_error("Can't write CodeCoverage report into $current_report");
344
                    }
345
                }
346
347
                // This will either lock the existing report for writing and return it along with a file pointer,
348
                // or return a fresh PHP_CodeCoverage object without a file pointer. We'll merge the current request
349
                // into that coverage object, write it to disk, and release the lock. By doing this in the end of
350
                // the request, we avoid this scenario, where Request 2 overwrites the changes from Request 1:
351
                //
352
                //             Time ->
353
                // Request 1 [ <read>               <write>          ]
354
                // Request 2 [         <read>                <write> ]
355
                //
356
                // In addition, by locking the file for exclusive writing, we make sure no other request try to
357
                // read/write to the file at the same time as this request (leading to a corrupt file). flock() is a
358
                // blocking call, so it waits until an exclusive lock can be acquired before continuing.
359
360
                list($existingCodeCoverage, $file) = __c3_factory($current_report, true);
361
                $existingCodeCoverage->merge($codeCoverage);
362
363
                if ($file === null) {
364
                    file_put_contents($current_report, serialize($existingCodeCoverage), LOCK_EX);
365
                } else {
366
                    fseek($file, 0);
367
                    fwrite($file, serialize($existingCodeCoverage));
368
                    fflush($file);
369
                    flock($file, LOCK_UN);
370
                    fclose($file);
371
                }
372
            }
373
        );
374
    }
375
}
376
377
// @codeCoverageIgnoreEnd
378