microtime. * * @var array */ private static array $marks = []; /** * Arbitrary notes recorded this request. * * @var array */ private static array $notes = []; /** * Wall-clock time at the first mark. * * @var float|null */ private static ?float $startTime = null; /** * Query count at the first mark. * * @var int|null */ private static ?int $startQueries = null; /** * Included-file count at the first mark. * * @var int|null */ private static ?int $startFiles = null; /** * Is profiling switched on? */ public static function enabled(): bool { return defined( 'RAHKAR_WP_PROFILE' ) && RAHKAR_WP_PROFILE; } /** * Record a named point in the boot sequence. * * @param string $label Mark name. */ public static function mark( string $label ): void { if ( ! self::enabled() ) { return; } if ( null === self::$startTime ) { self::$startTime = microtime( true ); self::$startQueries = self::queryCount(); self::$startFiles = count( get_included_files() ); } self::$marks[ $label ] = microtime( true ); } /** * Attach a value to the profile output. * * @param string $key Note name. * @param mixed $value Note value. */ public static function note( string $key, mixed $value ): void { if ( ! self::enabled() ) { return; } self::$notes[ $key ] = $value; } /** * Collect the profile for this request. * * @return array{ * context: list, * boot_ms: float, * queries_added: int, * files_loaded: int, * manifest_bytes: int, * marks: array, * notes: array * } */ public static function report(): array { $last = self::$marks ? max( self::$marks ) : (float) ( self::$startTime ?? 0.0 ); return [ 'context' => Context::describe(), 'boot_ms' => round( ( $last - (float) ( self::$startTime ?? $last ) ) * 1000, 3 ), 'queries_added' => self::queryCount() - (int) ( self::$startQueries ?? 0 ), 'files_loaded' => count( get_included_files() ) - (int) ( self::$startFiles ?? 0 ), 'manifest_bytes' => Manifest::size(), 'marks' => self::relativeMarks(), 'notes' => self::$notes, ]; } /** * Emit the profile as an HTML comment. * * Hooked to `shutdown` by {@see \Rahkar\WP\Lifecycle} when profiling is on. * `bin/perf-check.php` scrapes this marker out of the response. * * @internal Hooked callback. */ public static function emit(): void { if ( ! self::enabled() || Context::is( Context::AJAX | Context::REST | Context::CRON ) ) { return; } $json = wp_json_encode( self::report() ); if ( false === $json ) { return; } echo "\n\n"; } /** * Marks, converted to milliseconds since the first mark. * * @return array */ private static function relativeMarks(): array { $base = (float) ( self::$startTime ?? 0.0 ); $result = []; foreach ( self::$marks as $label => $time ) { $result[ $label ] = round( ( $time - $base ) * 1000, 3 ); } return $result; } /** * Queries run so far, when `SAVEQUERIES` is unavailable. */ private static function queryCount(): int { global $wpdb; return isset( $wpdb->num_queries ) ? (int) $wpdb->num_queries : 0; } }