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 / diagnostics / DecisiveRecordDiscriminatorContract.php

DecisiveRecordDiscriminatorContract.php in 404 Solution trunk, at includes/diagnostics/DecisiveRecordDiscriminatorContract.php

445 lines 19.6 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 require_once __DIR__ . '/DecisiveRecordQueryFilterFamilies.php';
8 require_once __DIR__ . '/DecisiveRecordFailureFamilies.php';
9 require_once __DIR__ . '/DecisiveRecordRenderFamilies.php';
10 require_once __DIR__ . '/DecisiveRecordRenderOptionIoFamilies.php';
11 require_once __DIR__ . '/DecisiveRecordStatusCountFamilies.php';
12
13 /**
14 * Canonical discriminator-field and conditional-presence contract catalog.
15 *
16 * The decisive-record manifest owns event-family enrollment. This catalog
17 * owns the executable record-shape rules consumed by PHP and browser-backed
18 * principal gates, independently of the evaluator that interprets them.
19 */
20 final class ABJ_404_Solution_DecisiveRecordDiscriminatorContract {
21 /** @return array<string, array<string, mixed>> */
22 public static function contracts(): array {
23 $hookRequirements = self::hookRequirements();
24 $cacheRequirements = self::cacheRequirements();
25 $resolutionRequirements = self::detachResolutionRequirements();
26 $operationFields = self::detachOperationFields();
27 $transientRequirements = self::detachTransientRequirements($operationFields);
28 $probeFields = self::cacheProbeFields();
29 $responseCallbackFields = array(
30 'operation_id',
31 'filter_hook',
32 'registered_hook',
33 'hook',
34 'callback',
35 'source',
36 'priority',
37 'callback_ordinal',
38 );
39 $responseCallbackIdentityFields = array(
40 'operation_id',
41 'filter_hook',
42 'registered_hook',
43 'hook',
44 'callback',
45 'source',
46 );
47 $responseCallbackActivation = array(
48 'any' => array(
49 array(
50 'event' => 'response_control_filter_dispatch_end',
51 'field' => 'callbacks_attributed',
52 'operator' => 'greater_than',
53 'value' => 0,
54 ),
55 array(
56 'fact' => 'response_control_filter_callbacks_expected',
57 'equals' => true,
58 ),
59 ),
60 );
61 $contracts = array(
62 'hook_lifecycle_consumers' => array(
63 'profiles' => array('ordinary_table'),
64 'requirements' => $hookRequirements,
65 ),
66 'cache_probe_boundaries' => array(
67 'profiles' => array('ordinary_table'),
68 'requirements' => $cacheRequirements,
69 ),
70 'post_cap_translation_callback' => array(
71 'profiles' => array('post_cap_translation_callback'),
72 'requirements' => array(array(
73 'id' => 'post_cap_translation_callback_identity',
74 'event' => 'active_operation_breadcrumb',
75 'match' => array(
76 'operation_state' => 'armed',
77 'boundary' => 'table_prelude_hook_callback',
78 'state' => 'active',
79 ),
80 'required_fields' => array(
81 'operation_id', 'hook', 'callback', 'source', 'locale',
82 ),
83 'non_empty_fields' => array(
84 'operation_id', 'hook', 'callback', 'source', 'locale',
85 ),
86 'all_matches' => true,
87 'activation' => array(
88 'any' => array(
89 array('fact' => 'post_cap_translation_callback_expected', 'equals' => true),
90 array('event' => 'table_prelude_hook_callback_capped'),
91 ),
92 ),
93 )),
94 ),
95 'cache_probe_unmatched_support' => array(
96 'profiles' => array('unmatched_cache_probe_support'),
97 'requirements' => array(array(
98 'id' => 'unmatched_cache_probe_identity',
99 'event' => 'cache_metrics_probe_start',
100 'required_fields' => $probeFields,
101 'non_empty_fields' => $probeFields,
102 'all_matches' => true,
103 'unmatched_end_event' => 'cache_metrics_probe_end',
104 'activation' => array(
105 'fact' => 'unmatched_cache_probe_expected',
106 'equals' => true,
107 ),
108 )),
109 ),
110 'response_control_filter_callbacks' => array(
111 'profiles' => array('ordinary_table', 'response_control_filter_callbacks'),
112 'requirements' => array(
113 array(
114 'id' => 'response_control_filter_callback_start_identity',
115 'event' => 'response_control_filter_callback_start',
116 'required_fields' => $responseCallbackFields,
117 'non_empty_fields' => $responseCallbackIdentityFields,
118 'field_types' => array(
119 'priority' => 'integer',
120 'callback_ordinal' => 'positive_integer',
121 ),
122 'all_matches' => true,
123 'activation' => $responseCallbackActivation,
124 ),
125 array(
126 'id' => 'response_control_filter_callback_end_identity',
127 'event' => 'response_control_filter_callback_end',
128 'required_fields' => $responseCallbackFields,
129 'non_empty_fields' => $responseCallbackIdentityFields,
130 'field_types' => array(
131 'priority' => 'integer',
132 'callback_ordinal' => 'positive_integer',
133 ),
134 'all_matches' => true,
135 'activation' => $responseCallbackActivation,
136 ),
137 ),
138 ),
139 'unmatched_response_control_callback_support' => array(
140 'profiles' => array('unmatched_response_control_callback_support'),
141 'requirements' => array(array(
142 'id' => 'unmatched_response_control_callback_identity',
143 'event' => 'response_control_filter_callback_start',
144 'required_fields' => $responseCallbackFields,
145 'non_empty_fields' => $responseCallbackIdentityFields,
146 'field_types' => array(
147 'priority' => 'integer',
148 'callback_ordinal' => 'positive_integer',
149 ),
150 'all_matches' => true,
151 'unmatched_end_event' => 'response_control_filter_callback_end',
152 'activation' => array(
153 'fact' => 'unmatched_response_control_callback_expected',
154 'equals' => true,
155 ),
156 )),
157 ),
158 'detach_ab_resolution_boundaries' => array(
159 'profiles' => array('ordinary_table'),
160 'requirements' => $resolutionRequirements,
161 ),
162 'detach_ab_transient_operations' => array(
163 'profiles' => array('ordinary_table'),
164 'requirements' => $transientRequirements,
165 ),
166 'unmatched_detach_ab_support' => array(
167 'profiles' => array('unmatched_detach_ab_support'),
168 'requirements' => array(array(
169 'id' => 'unmatched_detach_ab_operation_identity',
170 'event' => 'detach_ab_operation_start',
171 'required_fields' => $operationFields,
172 'non_empty_fields' => $operationFields,
173 'all_matches' => true,
174 'unmatched_end_event' => 'detach_ab_operation_end',
175 'activation' => array(
176 'fact' => 'unmatched_detach_ab_expected',
177 'equals' => true,
178 ),
179 )),
180 ),
181 );
182 return array_merge(
183 $contracts,
184 ABJ_404_Solution_DecisiveRecordQueryFilterFamilies::contracts(),
185 ABJ_404_Solution_DecisiveRecordFailureFamilies::contracts(),
186 ABJ_404_Solution_DecisiveRecordRenderFamilies::contracts(),
187 ABJ_404_Solution_DecisiveRecordRenderOptionIoFamilies::contracts(),
188 ABJ_404_Solution_DecisiveRecordStatusCountFamilies::contracts()
189 );
190 }
191
192 /** @return array<int, array<string, mixed>> */
193 private static function hookRequirements(): array {
194 $positionFields = array(
195 'operation_id', 'component', 'phase', 'hook', 'priority', 'callback_ordinal',
196 );
197 $hookRequirements = array(array(
198 'id' => 'all_hook_lifecycle_positions',
199 'event' => 'hook_instrumentation_lifecycle_start',
200 'required_fields' => $positionFields,
201 'all_matches' => true,
202 'activation' => array('event' => 'hook_instrumentation_lifecycle_start'),
203 ));
204 $consumers = array(
205 'option_persistence' => array('option_hook_instrumentation', 'callbacks_attributed'),
206 'table_renderer_prelude' => array('table_prelude_instrumentation', 'callbacks_attributed'),
207 'table_render_translation' => array(
208 'render_translation_scope_end', 'callbacks_attributed',
209 ),
210 'database_query_filter' => array(
211 'query_filter_instrumentation', 'callbacks_attributed',
212 ),
213 'row_render' => array('row_operation_instrumentation', 'all_callbacks_attributed'),
214 'response_control_filter' => array(
215 'response_control_filter_dispatch_end', 'callbacks_attributed',
216 ),
217 );
218 foreach ($consumers as $component => $sentinel) {
219 $activation = array('event' => $sentinel[0]);
220 $base = array(
221 'event' => 'hook_instrumentation_lifecycle_start',
222 'match' => array('component' => $component, 'phase' => 'install'),
223 'required_fields' => $positionFields,
224 'all_matches' => true,
225 'activation' => $activation,
226 );
227 $hookRequirements[] = array_merge($base, array('id' => $component . '_install'));
228 $positionActivation = array(
229 'event' => $sentinel[0],
230 'field' => $sentinel[1],
231 'operator' => 'greater_than',
232 'value' => 0,
233 );
234 $positionTypes = array('priority' => 'integer', 'callback_ordinal' => 'positive_integer');
235 $hookRequirements[] = array_merge($base, array(
236 'id' => $component . '_install_position',
237 'activation' => $positionActivation,
238 'field_types' => $positionTypes,
239 'all_matches' => false,
240 ));
241 $hookRequirements[] = array(
242 'id' => $component . '_restore_position',
243 'event' => 'hook_instrumentation_lifecycle_start',
244 'match' => array('component' => $component, 'phase' => 'restore'),
245 'required_fields' => $positionFields,
246 'field_types' => $positionTypes,
247 'activation' => $positionActivation,
248 );
249 }
250 $dynamicRegistrations = array(
251 'render_option_io' => array(
252 'event' => 'render_option_io_instrumentation',
253 'field' => 'query_boundary',
254 'value' => 'ready',
255 ),
256 'row_render' => array(
257 'event' => 'row_operation_instrumentation',
258 'field' => 'hook_boundary',
259 'value' => 'ready',
260 ),
261 'table_render_translation' => array('event' => 'render_translation_scope_end'),
262 'database_query_filter' => array(
263 'event' => 'query_filter_instrumentation',
264 'field' => 'driver_sentinel',
265 'value' => 'registered',
266 ),
267 );
268 foreach ($dynamicRegistrations as $component => $activation) {
269 foreach (array('registration', 'removal') as $phase) {
270 $hookRequirements[] = array(
271 'id' => $component . '_' . $phase,
272 'event' => 'hook_instrumentation_lifecycle_start',
273 'match' => array('component' => $component, 'phase' => $phase),
274 'required_fields' => $positionFields,
275 'all_matches' => true,
276 'activation' => $activation,
277 );
278 }
279 }
280 return $hookRequirements;
281 }
282
283 /** @return array<int, array<string, mixed>> */
284 private static function cacheRequirements(): array {
285 $cacheRequirements = array();
286 $probeFields = self::cacheProbeFields();
287 foreach (array('initial', 'progress', 'finish') as $phase) {
288 $cachePresent = array('fact' => 'cache_object_present', 'equals' => true);
289 $phaseSentinel = $phase === 'initial'
290 ? array('event' => 'row_loop_start')
291 : array(
292 'event' => $phase === 'progress' ? 'row_loop_progress' : 'row_loop_end',
293 'field' => 'cache_src',
294 'operator' => 'not_equals',
295 'value' => 'error',
296 );
297 $capabilityExpected = array('all' => array($cachePresent, $phaseSentinel));
298 foreach (array('start', 'end') as $edge) {
299 $cacheRequirements[] = array(
300 'id' => 'cache_capability_' . $phase . '_' . $edge,
301 'event' => 'cache_metrics_probe_' . $edge,
302 'match' => array('source' => 'metrics_capability', 'phase' => $phase),
303 'required_fields' => $probeFields,
304 'all_matches' => true,
305 'activation' => $capabilityExpected,
306 );
307 }
308 $metricsAvailable = array(
309 'event' => 'cache_metrics_probe_end',
310 'match' => array('source' => 'metrics_capability', 'phase' => $phase),
311 'field' => 'result',
312 'value' => 'available',
313 );
314 foreach (array('start', 'end') as $edge) {
315 $cacheRequirements[] = array(
316 'id' => 'cache_metrics_' . $phase . '_' . $edge,
317 'event' => 'cache_metrics_probe_' . $edge,
318 'match' => array('source' => 'metrics', 'phase' => $phase),
319 'required_fields' => $probeFields,
320 'all_matches' => true,
321 'activation' => $metricsAvailable,
322 );
323 }
324 $countersRequired = array(
325 'any' => array(
326 array(
327 'event' => 'cache_metrics_probe_end',
328 'match' => array('source' => 'metrics_capability', 'phase' => $phase),
329 'field' => 'result',
330 'value' => 'unavailable',
331 ),
332 array(
333 'event' => 'cache_metrics_probe_end',
334 'match' => array('source' => 'metrics', 'phase' => $phase),
335 'field' => 'result',
336 'value' => 'snapshot_unavailable',
337 ),
338 ),
339 );
340 foreach (array('start', 'end') as $edge) {
341 $cacheRequirements[] = array(
342 'id' => 'cache_counters_' . $phase . '_' . $edge,
343 'event' => 'cache_metrics_probe_' . $edge,
344 'match' => array('source' => 'counters', 'phase' => $phase),
345 'required_fields' => $probeFields,
346 'all_matches' => true,
347 'activation' => $countersRequired,
348 );
349 }
350 }
351 $cacheRequirements[] = array(
352 'id' => 'no_cache_object_sentinel',
353 'event' => 'row_loop_end',
354 'match' => array('cache_src' => 'none'),
355 'required_fields' => array('cache_src', 'cache_calls', 'cache_ms'),
356 'activation' => array('fact' => 'cache_object_present', 'equals' => false),
357 );
358 $cacheRequirements[] = array(
359 'id' => 'cache_probe_error_sentinel',
360 'event' => 'row_loop_end',
361 'match' => array('cache_src' => 'error'),
362 'required_fields' => array('cache_src', 'cache_calls', 'cache_ms'),
363 'activation' => array(
364 'event' => 'cache_metrics_probe_end',
365 'field' => 'status',
366 'value' => 'error',
367 ),
368 );
369 return $cacheRequirements;
370 }
371
372 /** @return array<int, string> */
373 private static function cacheProbeFields(): array {
374 return array('operation_id', 'source', 'phase');
375 }
376
377 /** @return array<int, array<string, mixed>> */
378 private static function detachResolutionRequirements(): array {
379 $resolutionFields = array(
380 'operation_id', 'operation', 'session_state', 'part', 'payload_key',
381 );
382 return array(
383 array(
384 'id' => 'detach_ab_resolution_start_identity',
385 'event' => 'detach_ab_resolution_start',
386 'required_fields' => $resolutionFields,
387 'non_empty_fields' => $resolutionFields,
388 'all_matches' => true,
389 'activation' => array('event' => 'finish_request'),
390 ),
391 array(
392 'id' => 'detach_ab_resolution_end_identity',
393 'event' => 'detach_ab_resolution_end',
394 'required_fields' => array_merge($resolutionFields, array(
395 'status', 'mode', 'diagnostic_enabled', 'counter_status',
396 )),
397 'non_empty_fields' => array_merge($resolutionFields, array(
398 'status', 'mode', 'counter_status',
399 )),
400 'all_matches' => true,
401 'activation' => array('event' => 'finish_request'),
402 ),
403 );
404 }
405
406 /** @return array<int, string> */
407 private static function detachOperationFields(): array {
408 return array(
409 'operation_id', 'operation', 'transient_key', 'cache_backend',
410 'cache_backend_class', 'cache_capabilities',
411 );
412 }
413
414 /**
415 * @param array<int, string> $operationFields
416 * @return array<int, array<string, mixed>>
417 */
418 private static function detachTransientRequirements(array $operationFields): array {
419 $operationActivation = array(
420 'event' => 'detach_ab_resolution_end',
421 'field' => 'counter_status',
422 'value' => 'attempt_resolved',
423 );
424 $transientRequirements = array();
425 foreach (array('get_transient', 'set_transient') as $operation) {
426 foreach (array('start', 'end') as $edge) {
427 $required = $operationFields;
428 if ($edge === 'end') {
429 $required = array_merge($required, array('status', 'result'));
430 }
431 $transientRequirements[] = array(
432 'id' => 'detach_ab_' . $operation . '_' . $edge,
433 'event' => 'detach_ab_operation_' . $edge,
434 'match' => array('operation' => $operation),
435 'required_fields' => $required,
436 'non_empty_fields' => $required,
437 'all_matches' => true,
438 'activation' => $operationActivation,
439 );
440 }
441 }
442 return $transientRequirements;
443 }
444 }
445