Code Coverage
 
Lines
Branches
Paths
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
2 / 2
CRAP
100.00% covered (success)
100.00%
1 / 1
Fallback
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
2 / 2
7
100.00% covered (success)
100.00%
1 / 1
 emit
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
3
 discardBuffers
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
4
1<?php
2
3declare(strict_types=1);
4
5namespace Celema\Core\Emitter;
6
7use Psr\Http\Message\ResponseInterface as Response;
8use Throwable;
9
10/**
11 * Last resort for a request that failed outside the error handler.
12 *
13 * @internal
14 */
15final class Fallback
16{
17    /**
18     * Emits the response if nothing was sent yet. Output buffers the request
19     * opened are discarded first. If the emitter still fails, for example
20     * because the request wrote output into a buffer it did not open or one
21     * that cannot be cleaned, the status is set to 500 when possible and
22     * that output becomes the body.
23     */
24    public static function emit(
25        Emitter $emitter,
26        Response $response,
27        int $bufferLevel,
28        bool $withoutBody = false,
29    ): void {
30        self::discardBuffers($bufferLevel);
31
32        try {
33            $emitter->emit($response, $withoutBody);
34        } catch (Throwable) {
35            if (!headers_sent()) {
36                http_response_code($response->getStatusCode());
37            }
38        }
39    }
40
41    /**
42     * A buffer opened without the removable flag cannot be ended:
43     * ob_end_clean() fails and leaves it in place. The cleanup stops there,
44     * after discarding its output if allowed, and the response goes into
45     * that buffer, which PHP sends when the request ends.
46     */
47    private static function discardBuffers(int $bufferLevel): void
48    {
49        while (ob_get_level() > $bufferLevel) {
50            $flags = (int) (ob_get_status()['flags'] ?? 0);
51
52            if (($flags & PHP_OUTPUT_HANDLER_REMOVABLE) === 0) {
53                if (($flags & PHP_OUTPUT_HANDLER_CLEANABLE) !== 0) {
54                    ob_clean();
55                }
56
57                return;
58            }
59
60            ob_end_clean();
61        }
62    }
63}

Branches

Below are the source code lines that represent each code branch as identified by Xdebug. Please note a branch is not necessarily coterminous with a line, a line may contain multiple branches and therefore show up more than once. Please also be aware that some branches may be implicit rather than explicit, e.g. an if statement always has an else as part of its logical flow even if you didn't write one.

Fallback->discardBuffers
47    private static function discardBuffers(int $bufferLevel): void
48    {
49        while (ob_get_level() > $bufferLevel) {
50            $flags = (int) (ob_get_status()['flags'] ?? 0);
51
52            if (($flags & PHP_OUTPUT_HANDLER_REMOVABLE) === 0) {
53                if (($flags & PHP_OUTPUT_HANDLER_CLEANABLE) !== 0) {
54                    ob_clean();
55                }
56
57                return;
57                return;
49        while (ob_get_level() > $bufferLevel) {
50            $flags = (int) (ob_get_status()['flags'] ?? 0);
51
52            if (($flags & PHP_OUTPUT_HANDLER_REMOVABLE) === 0) {
53                if (($flags & PHP_OUTPUT_HANDLER_CLEANABLE) !== 0) {
54                    ob_clean();
55                }
56
57                return;
58            }
59
60            ob_end_clean();
49        while (ob_get_level() > $bufferLevel) {
62    }
Fallback->emit
25        Emitter $emitter,
26        Response $response,
27        int $bufferLevel,
28        bool $withoutBody = false,
29    ): void {
30        self::discardBuffers($bufferLevel);
31
32        try {
33            $emitter->emit($response, $withoutBody);
34        } catch (Throwable) {
35            if (!headers_sent()) {
36                http_response_code($response->getStatusCode());
37            }
38        }
39    }
39    }