PluginProbe
404 Solution / trunk
404 Solution vtrunk
4.3.5 4.3.4 4.3.3 4.3.2 4.3.1 4.3.0 4.2.0 4.1.19 4.1.18 4.1.17 4.1.16 4.1.15 4.1.13 4.1.12 4.1.11 4.1.10 4.1.9 4.1.8 4.1.7 4.1.6 4.1.5 4.1.4 4.1.3 trunk 2.30.0 All 109 releases
404-solution / includes / ajax / CanarySizeTarget.php

CanarySizeTarget.php in 404 Solution trunk, at includes/ajax/CanarySizeTarget.php

93 lines 4.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 if (!defined('ABSPATH')) {
4 exit;
5 }
6
7 /**
8 * The byte count the canary size probes calibrate against, and where it came
9 * from.
10 *
11 * Split out of ABJ_404_Solution_AjaxCanaryStepRunner. Every other arm of that
12 * class emits a payload of a known size and times the round trip; this one
13 * emits nothing and times nothing. It answers a question about the SITE --
14 * how big is a real admin-table response here -- by arbitrating between two
15 * independent sources and naming which one answered. That is a policy
16 * decision, not a probe, and it sits beside the sources it arbitrates
17 * (ABJ_404_Solution_EncodedTableResponseSize,
18 * ABJ_404_Solution_MeasuredTableResponseSize) rather than among the probes
19 * that consume its answer.
20 *
21 * It lives in includes/ajax/ rather than includes/diagnostics/ because its
22 * fallback branch BUILDS a table response to measure it. deptrac's Diagnostics
23 * layer may not reach Presentation, and that rule is right: a class that
24 * renders a response has stopped being a recorder. This one genuinely does
25 * that, so it is classified where it belongs instead of being excepted from
26 * the rule. The recorded branch still reads only journal evidence.
27 *
28 * Recorded first, measured second, named either way. The recorded number is
29 * the exact size of the response that actually failed, so it always wins when
30 * it exists -- but it exists only on a site whose durable trace was already
31 * armed when the failure happened, and `debug_mode` is off by default on
32 * purpose (AjaxDiagnosticRequestPolicy::DIAGNOSTIC_TRACE_ACTIONS keeps
33 * `ajaxUpdatePaginationLinks` behind the 4.3.3 performance gate). The ladder
34 * self-arms, which is what gives the ladder its OWN evidence, but nothing can
35 * retroactively arm a request that has already run.
36 *
37 * So on the shipped default there is nothing to recover and the step used to
38 * answer with an absence, leaving the client to calibrate the whole size axis
39 * on a hardcoded default -- support report 2026-08-27 (Azure App Service,
40 * plugin 4.3.4) ran all ten steps and could neither confirm nor rule out size,
41 * because every probe ran at a size unrelated to this site's real response. A
42 * measurement is available on every site, so the absence is now the fallback's
43 * fallback.
44 *
45 * `realResponseRecordedSource` keeps the recorded channel's own answer whatever
46 * happens, so "measured because nothing was recorded" stays distinguishable
47 * from "measured because the trace named no table request" and from "recorded
48 * exactly". A number is never returned without the source that produced it.
49 */
50 final class ABJ_404_Solution_CanarySizeTarget {
51
52 /**
53 * Resolve the calibration size for one browser session and subpage.
54 *
55 * Keyed rather than positional: a session id and a subpage are both
56 * strings, so positionally a transposed call would query the journal for a
57 * subpage and the table for a session, and return a confidently wrong byte
58 * count with a plausible-looking source beside it. PHP 7.4 is the floor
59 * here, so named arguments are unavailable.
60 *
61 * @param array{subpage: string, session_id: string} $inputs
62 * @param array<string, mixed> $context Mutated in place by the build's stages.
63 * @return array<string, mixed>
64 */
65 public static function resolve(array $inputs, array &$context): array {
66 $sessionId = $inputs['session_id'];
67 $recorded = ABJ_404_Solution_AjaxStageDiagnostics::runStage($context, 'canary_size_target',
68 static function () use ($sessionId) {
69 return ABJ_404_Solution_EncodedTableResponseSize::forSession($sessionId);
70 });
71 if (is_int($recorded['bytes']) && $recorded['bytes'] > 0) {
72 return array(
73 'realResponseBytes' => $recorded['bytes'],
74 'realResponseBytesSource' => $recorded['source'],
75 'realResponseRequestId' => $recorded['request_id'],
76 'realResponseRecordedSource' => $recorded['source'],
77 );
78 }
79 // Deliberately NOT inside the stage above: the builder opens its own
80 // stage and trace stages are flat, so nesting would only mark
81 // canary_size_target `superseded`. See MeasuredTableResponseSize.
82 $measured = ABJ_404_Solution_MeasuredTableResponseSize::forSubpage($inputs['subpage'], $context);
83 return array(
84 'realResponseBytes' => $measured['bytes'],
85 'realResponseBytesSource' => $measured['source'],
86 // No request id: a measurement is of this site's table, not of any
87 // one request, and borrowing a request id would imply otherwise.
88 'realResponseRequestId' => '',
89 'realResponseRecordedSource' => $recorded['source'],
90 );
91 }
92 }
93