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 / AjaxFrequentCheckpointWriter.php

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

125 lines 4.3 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 * Writes lightweight checkpoint records and caches their resolved request path.
9 *
10 * The cache lets callbacks executing inside wpdb reuse the path resolved by
11 * the pre-query probe. Re-entering wp_upload_dir() or WordPress filters from a
12 * query callback can issue nested SQL and recursively dispatch the callback
13 * being measured.
14 */
15 final class ABJ_404_Solution_AjaxFrequentCheckpointWriter {
16
17 /** @var string */
18 private static $resolvedRequestId = '';
19
20 /** @var string */
21 private static $resolvedDirectory = '';
22
23 /** @var int */
24 private static $checkpointSequence = 0;
25
26 public static function rememberResolvedDirectory(string $requestId, string $directory): void {
27 if ($requestId === '') {
28 return;
29 }
30 self::$resolvedRequestId = $requestId;
31 self::$resolvedDirectory = $directory;
32 }
33
34 public static function resolvedDirectoryForRequest(string $requestId): string {
35 return $requestId !== '' && $requestId === self::$resolvedRequestId
36 ? self::$resolvedDirectory
37 : '';
38 }
39
40 /**
41 * Whether directory resolution already ran for this request.
42 *
43 * Kept separate from resolvedDirectoryForRequest() because an empty
44 * directory is a valid cached failure. Re-running WordPress filters after
45 * that failure can recurse into the same database boundary being traced.
46 */
47 public static function hasResolvedDirectoryForRequest(string $requestId): bool {
48 return $requestId !== '' && $requestId === self::$resolvedRequestId;
49 }
50
51 public static function resetForTests(): void {
52 self::$resolvedRequestId = '';
53 self::$resolvedDirectory = '';
54 self::$checkpointSequence = 0;
55 }
56
57 /**
58 * @param array<string, mixed> $fields
59 */
60 public static function append(
61 string $requestId,
62 string $event,
63 array $fields,
64 string $directory,
65 bool $directoryWasResolved,
66 string $checkpointId = ''
67 ): void {
68 if ($checkpointId === '') {
69 $checkpointId = self::checkpointId();
70 ABJ_404_Solution_CheckpointIntentStore::append(
71 ABJ_404_Solution_CheckpointRecordFactory::intent(array(
72 'request_id' => $requestId,
73 'event' => $event,
74 'checkpoint_id' => $checkpointId,
75 'hrtime_ns' => function_exists('hrtime') ? (int)hrtime(true) : null,
76 'pid' => ABJ_404_Solution_PhpRuntimeCapabilityAdapter::processId(),
77 ))
78 );
79 }
80 if ($directory === '') {
81 return;
82 }
83 if (!$directoryWasResolved
84 && (!class_exists('ABJ_404_Solution_FileSystemService')
85 || !ABJ_404_Solution_FileSystemService::createDirectoryWithErrorMessages($directory))) {
86 return;
87 }
88 ABJ_404_Solution_CheckpointJournalWriter::append($directory, array_merge(
89 $fields,
90 ABJ_404_Solution_CheckpointRecordFactory::frequent(array(
91 'ts' => self::nowFloat(),
92 'hrtime_ns' => function_exists('hrtime') ? (int)hrtime(true) : null,
93 'request_id' => $requestId,
94 'event' => $event,
95 'checkpoint_id' => $checkpointId,
96 'pid' => ABJ_404_Solution_PhpRuntimeCapabilityAdapter::processId(),
97 ))
98 ));
99 }
100
101 private static function checkpointId(): string {
102 self::$checkpointSequence++;
103 $startedNs = function_exists('hrtime') ? (int)hrtime(true) : 0;
104 return self::alphabeticHex(
105 ABJ_404_Solution_PhpRuntimeCapabilityAdapter::processNumericToken()
106 ) . '-'
107 . self::alphabeticHex($startedNs) . '-f'
108 . self::alphabeticHex(self::$checkpointSequence);
109 }
110
111 private static function alphabeticHex(int $value): string {
112 return strtr(dechex($value), '0123456789abcdef', 'ghijklmnopqrstuv');
113 }
114
115 private static function nowFloat(): ?float {
116 if (function_exists('abj_clock')) {
117 return abj_clock()->nowFloat();
118 }
119 if (class_exists('ABJ_404_Solution_SystemClock')) {
120 return (new ABJ_404_Solution_SystemClock())->nowFloat();
121 }
122 return null;
123 }
124 }
125