| 1 |
<?php |
| 2 |
|
| 3 |
if (!defined('ABSPATH')) { |
| 4 |
exit; |
| 5 |
} |
| 6 |
|
| 7 |
/** |
| 8 |
* Ask this response's transport not to compress or transform it, and report |
| 9 |
* whether each request actually took effect. |
| 10 |
* |
| 11 |
* Two mechanisms, because two different parties compress. `Cache-Control: |
| 12 |
* no-transform` asks an intermediary (LiteSpeed, Cloudflare, a caching reverse |
| 13 |
* proxy) to leave the bytes alone; `zlib.output_compression` is PHP's own |
| 14 |
* output handler. Suppressing one and not the other still yields a compressed |
| 15 |
* response, so the two outcomes are reported separately and `applied` is the |
| 16 |
* conjunction. |
| 17 |
* |
| 18 |
* BOTH are refusable at runtime, and neither refusal raises anything a caller |
| 19 |
* would notice: |
| 20 |
* |
| 21 |
* - Once any byte of output has been emitted, headers_sent() is true, the |
| 22 |
* header cannot be set, and ini_set() returns false for this directive with |
| 23 |
* "Cannot change zlib.output_compression - headers already sent". |
| 24 |
* - A host with ini_set in disable_functions refuses the second half outright |
| 25 |
* (see ABJ_404_Solution_PhpRuntimeCapabilityAdapter), which is a live user |
| 26 |
* environment, not a hypothetical one. |
| 27 |
* |
| 28 |
* The outcome is returned rather than discarded because the caller is a |
| 29 |
* diagnostic instrument. A canary rung that suppressed nothing but reports |
| 30 |
* itself as the suppressed rung does not merely lose a probe: it turns the |
| 31 |
* ladder's central comparison -- adjacent rungs differing by exactly one |
| 32 |
* variable -- into a comparison of a step against itself, while every consumer |
| 33 |
* still reads it as evidence about compression. |
| 34 |
*/ |
| 35 |
final class ABJ_404_Solution_ResponseCompressionSuppression { |
| 36 |
|
| 37 |
/** |
| 38 |
* Request suppression from both parties and report what each one did. |
| 39 |
* |
| 40 |
* @return array{noTransformHeader: bool, zlibOutputCompression: bool, applied: bool} |
| 41 |
* `noTransformHeader`: the no-transform header was set on this response. |
| 42 |
* `zlibOutputCompression`: PHP's output compression is now off (a |
| 43 |
* directive that was already off still counts -- the state is what the |
| 44 |
* probe depends on, not whether this call changed it). |
| 45 |
* `applied`: both, so the response is genuinely unsuppressed-by-nobody. |
| 46 |
*/ |
| 47 |
public static function apply(): array { |
| 48 |
$noTransformHeader = false; |
| 49 |
if (!headers_sent()) { |
| 50 |
header('Cache-Control: no-transform'); |
| 51 |
$noTransformHeader = true; |
| 52 |
} |
| 53 |
// setIni() returns the PREVIOUS value on success and false on refusal, |
| 54 |
// so `!== false` is the test: an empty-string previous value means the |
| 55 |
// directive was already off, which is success, while a loose truthy |
| 56 |
// check would read it as failure. |
| 57 |
$zlibDisabled = ABJ_404_Solution_PhpRuntimeCapabilityAdapter::setIni( |
| 58 |
array('directive' => 'zlib.output_compression', 'value' => '0')) !== false; |
| 59 |
|
| 60 |
return array( |
| 61 |
'noTransformHeader' => $noTransformHeader, |
| 62 |
'zlibOutputCompression' => $zlibDisabled, |
| 63 |
'applied' => $noTransformHeader && $zlibDisabled, |
| 64 |
); |
| 65 |
} |
| 66 |
} |
| 67 |
|