Completed
Push — master ( 8cccfe...3ec575 )
by Oleg
02:14
created

c3.php ➔ __c3_build_crap4j_report()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 2
dl 0
loc 7
rs 9.4285
c 0
b 0
f 0
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]
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);
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;
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