PluginProbe
Templately – Elementor & Gutenberg Template Library: 6500+ Free & Pro Ready Templates And Cloud! / 3.8.0
Templately – Elementor & Gutenberg Template Library: 6500+ Free & Pro Ready Templates And Cloud! v3.8.0
3.8.0 3.7.5 3.7.4 3.7.3 3.7.2 1-final 3.7.1 3.7.0 3.6.8 3.6.7 3.6.6 3.6.5 3.6.4 3.6.3 3.6.2 3.6.1 3.0.3 3.0.4 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1.0 3.1.1 All 112 releases
templately / modules / full-site-import / Abilities / Support / FsiStatusNormalizer.php

FsiStatusNormalizer.php in Templately – Elementor & Gutenberg Template Library: 6500+ Free & Pro Ready Templates And Cloud! 3.8.0, at modules/full-site-import/Abilities/Support/FsiStatusNormalizer.php

167 lines 5.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Maps the raw signals of an FSI time-slice into the normalized MCP status
4 * enum (spec 042 FR-002/FR-003/FR-004, research.md §4, data-model.md).
5 *
6 * PURE: takes already-fetched inputs (the SSE events returned by the latest
7 * `import` slice, the SessionData row, and the log tail) and returns a plain
8 * array. No I/O, so it is unit-testable in isolation (T005).
9 *
10 * The FSI pipeline (Constitution V exception taxonomy) emits exactly these
11 * terminal / again signals, so the mapping is total:
12 * - action:'complete' → complete (carries `results` summary)
13 * - action:'error' retry:true → needs_retry (RetryableErrorException)
14 * - action:'error' retry:false → failed (Non-retirable / Unknown)
15 * - action:'continue' / a slice ran with no terminal event → running
16 * - empty session row (7-day cleanup) or unknown handle → expired
17 *
18 * @package Templately\Modules\FullSiteImport\Abilities\Support
19 */
20
21 namespace Templately\Modules\FullSiteImport\Abilities\Support;
22
23 class FsiStatusNormalizer {
24
25 const STATUS_RUNNING = 'running';
26 const STATUS_NEEDS_RETRY = 'needs_retry';
27 const STATUS_FAILED = 'failed';
28 const STATUS_COMPLETE = 'complete';
29 const STATUS_EXPIRED = 'expired';
30
31 /**
32 * @param array $sse_events Parsed SSE event objects from the latest slice.
33 * @param mixed $session_data The SessionData row for the handle (array), or empty.
34 * @param array $log_tail Recent log lines (for surfacing `log`).
35 * @return array {
36 * status, progress (0-100), phase, message?, summary?
37 * }
38 */
39 public static function normalize( array $sse_events, $session_data, array $log_tail = [] ): array {
40 // Unknown / expired handle: no persisted session to speak of.
41 if ( empty( $session_data ) || ! is_array( $session_data ) ) {
42 return [
43 'status' => self::STATUS_EXPIRED,
44 'progress' => 0,
45 'phase' => '',
46 ];
47 }
48
49 $terminal = self::find_terminal_event( $sse_events );
50 $progress = self::derive_progress( $sse_events, $session_data );
51 $phase = self::derive_phase( $sse_events, $session_data );
52
53 if ( $terminal && 'complete' === $terminal['action'] ) {
54 return [
55 'status' => self::STATUS_COMPLETE,
56 'progress' => 100,
57 'phase' => 'finalizer',
58 'summary' => $terminal['results'] ?? [],
59 ];
60 }
61
62 if ( $terminal && 'error' === $terminal['action'] ) {
63 $retry = ! empty( $terminal['retry'] );
64 return [
65 'status' => $retry ? self::STATUS_NEEDS_RETRY : self::STATUS_FAILED,
66 'progress' => $progress,
67 'phase' => $phase,
68 // Message + code only — never a stack trace (FR-006; the trace is
69 // already stripped server-side in RunsImport::import()).
70 'message' => isset( $terminal['message'] ) ? (string) $terminal['message'] : '',
71 ];
72 }
73
74 // A slice ran (or is between chunks) with no terminal event: still running.
75 return [
76 'status' => self::STATUS_RUNNING,
77 'progress' => $progress,
78 'phase' => $phase,
79 ];
80 }
81
82 /**
83 * The last complete/error event in the slice, if any (terminal wins over
84 * intermediate `continue`/`updateLog` events).
85 *
86 * @param array $sse_events
87 * @return array|null
88 */
89 private static function find_terminal_event( array $sse_events ): ?array {
90 $found = null;
91 foreach ( $sse_events as $event ) {
92 if ( ! is_array( $event ) ) {
93 continue;
94 }
95 $action = $event['action'] ?? '';
96 if ( 'complete' === $action || 'error' === $action ) {
97 $found = $event; // keep the last one
98 }
99 }
100 return $found;
101 }
102
103 /**
104 * Coarse 0-100 progress: prefer an explicit `progress` from the latest
105 * updateLog event; otherwise approximate from completed session steps.
106 *
107 * @param array $sse_events
108 * @param array $session_data
109 * @return int
110 */
111 private static function derive_progress( array $sse_events, array $session_data ): int {
112 $explicit = null;
113 foreach ( $sse_events as $event ) {
114 if ( is_array( $event ) && isset( $event['progress'] ) && is_numeric( $event['progress'] ) ) {
115 $explicit = (int) $event['progress'];
116 }
117 }
118 if ( null !== $explicit ) {
119 return max( 0, min( 100, $explicit ) );
120 }
121
122 // Fallback: fraction of known early setup steps completed.
123 $steps = [ 'create_log_dir', 'check_writing_permission', 'download_zip' ];
124 $progress = $session_data['progress'] ?? [];
125 $done = 0;
126 foreach ( $steps as $step ) {
127 if ( ! empty( $progress[ $step ] ) ) {
128 $done++;
129 }
130 }
131 // Cap the setup-only fallback well below 100 — content import follows.
132 return (int) round( ( $done / count( $steps ) ) * 40 );
133 }
134
135 /**
136 * Current phase label from the latest informative event, else the last
137 * completed session step.
138 *
139 * @param array $sse_events
140 * @param array $session_data
141 * @return string
142 */
143 private static function derive_phase( array $sse_events, array $session_data ): string {
144 $phase = '';
145 foreach ( $sse_events as $event ) {
146 if ( ! is_array( $event ) ) {
147 continue;
148 }
149 if ( ! empty( $event['info'] ) ) {
150 $phase = (string) $event['info'];
151 } elseif ( ! empty( $event['type'] ) && ! in_array( $event['type'], [ 'eventLog', 'updateLog', 'start' ], true ) ) {
152 $phase = (string) $event['type'];
153 }
154 }
155
156 if ( '' === $phase ) {
157 $progress = $session_data['progress'] ?? [];
158 if ( is_array( $progress ) && ! empty( $progress ) ) {
159 $keys = array_keys( array_filter( $progress ) );
160 $phase = end( $keys ) ?: '';
161 }
162 }
163
164 return $phase;
165 }
166 }
167