PluginProbe
Templately – Elementor & Gutenberg Template Library: 6500+ Free & Pro Ready Templates And Cloud! / trunk
Templately – Elementor & Gutenberg Template Library: 6500+ Free & Pro Ready Templates And Cloud! vtrunk
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 / includes / Utils / Response / TemplatelyError.php

TemplatelyError.php in Templately – Elementor & Gutenberg Template Library: 6500+ Free & Pro Ready Templates And Cloud! trunk, at includes/Utils/Response/TemplatelyError.php

183 lines 5.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Templately\Utils\Response;
4
5 use WP_Error;
6
7 /**
8 * The normalized error value object (spec 043).
9 *
10 * It deliberately **extends `WP_Error`** so every untouched `is_wp_error()` /
11 * `get_error_message()` call site in the plugin keeps working unchanged while
12 * consumers migrate onto the richer contract. The extra contract fields live in
13 * the `WP_Error` data bag, which is also exactly what the REST envelope
14 * serializes.
15 *
16 * `message` is ALWAYS plain text — never HTML (FR-010). The constructor strips
17 * tags defensively, because upstream messages have historically carried anchor
18 * markup and that is the reflected-XSS channel this spec closes.
19 */
20 class TemplatelyError extends WP_Error {
21
22 /**
23 * @param string $code An `ErrorCode` registry value.
24 * @param string $message Plain-text, translated, user-safe message.
25 * @param array $data Optional overrides: status, severity, retryable, fields, context, debug.
26 */
27 public function __construct( $code, $message = '', $data = [] ) {
28 $meta = ErrorCode::meta( $code );
29 $data = is_array( $data ) ? $data : [];
30
31 $normalized = [
32 'status' => isset( $data['status'] ) ? (int) $data['status'] : (int) $meta['status'],
33 'severity' => isset( $data['severity'] ) ? (string) $data['severity'] : $meta['severity'],
34 'retryable' => isset( $data['retryable'] ) ? (bool) $data['retryable'] : (bool) $meta['retryable'],
35 'fields' => isset( $data['fields'] ) && is_array( $data['fields'] ) ? $data['fields'] : [],
36 'context' => isset( $data['context'] ) && is_array( $data['context'] ) ? $data['context'] : [],
37 ];
38
39 // `debug` is carried only when the host is in debug mode; RestEnvelope
40 // strips it otherwise. It must NEVER contain file/line/trace (FR-008).
41 if ( isset( $data['debug'] ) && is_array( $data['debug'] ) ) {
42 $normalized['debug'] = $data['debug'];
43 }
44
45 // An empty message would reach the user as a blank error box. The registry
46 // already owns the wording for every code (and the normalizer already uses
47 // it on the inbound side), so fall back to it here rather than letting each
48 // construction site invent its own — or, as `AjaxResponder::guard()` did,
49 // send nothing at all.
50 $message = self::plain_text( $message );
51 if ( '' === $message ) {
52 $message = ErrorCode::default_message( $code );
53 }
54
55 parent::__construct( $code, $message, $normalized );
56 }
57
58 /**
59 * Reduce any upstream message to inert plain text (FR-010).
60 *
61 * Upstream sends anchor markup in some messages (e.g. `SiteNotConnected`).
62 * The link is re-composed on the client from `code` + `context`, never
63 * parsed back out of the message, so the tags are dropped here.
64 *
65 * @param mixed $message
66 * @return string
67 */
68 public static function plain_text( $message ) {
69 if ( is_array( $message ) ) {
70 $message = reset( $message );
71 }
72
73 if ( ! is_string( $message ) ) {
74 return '';
75 }
76
77 $message = wp_strip_all_tags( $message, true );
78 $message = html_entity_decode( $message, ENT_QUOTES, 'UTF-8' );
79
80 return trim( preg_replace( '/\s+/u', ' ', $message ) );
81 }
82
83 /**
84 * @return string the ErrorCode registry value.
85 */
86 public function code() {
87 return $this->get_error_code();
88 }
89
90 /**
91 * @return string plain-text message.
92 */
93 public function message() {
94 return (string) $this->get_error_message();
95 }
96
97 /**
98 * @return array the full normalized data bag.
99 */
100 public function data() {
101 $data = $this->get_error_data();
102 return is_array( $data ) ? $data : [];
103 }
104
105 /**
106 * @return int HTTP-equivalent status.
107 */
108 public function status() {
109 $data = $this->data();
110 return isset( $data['status'] ) ? (int) $data['status'] : 500;
111 }
112
113 /**
114 * @return string fatal|error|warning|info
115 */
116 public function severity() {
117 $data = $this->data();
118 return isset( $data['severity'] ) ? (string) $data['severity'] : 'error';
119 }
120
121 /**
122 * @return bool
123 */
124 public function is_retryable() {
125 $data = $this->data();
126 return ! empty( $data['retryable'] );
127 }
128
129 /**
130 * @return array { field: [messages] }
131 */
132 public function fields() {
133 $data = $this->data();
134 return isset( $data['fields'] ) && is_array( $data['fields'] ) ? $data['fields'] : [];
135 }
136
137 /**
138 * @return array structured params (status_text, retry_after, redirect, …).
139 */
140 public function context() {
141 $data = $this->data();
142 return isset( $data['context'] ) && is_array( $data['context'] ) ? $data['context'] : [];
143 }
144
145 /**
146 * Whether this error should never be surfaced to the user (FR-009a).
147 *
148 * @return bool
149 */
150 public function is_silent() {
151 return 'info' === $this->severity();
152 }
153
154 /**
155 * Adopt an arbitrary `WP_Error` into the contract without losing its code.
156 *
157 * Used at the seams where legacy code still hands us a plain `WP_Error`.
158 *
159 * @param WP_Error $error
160 * @param string $fallback_code an ErrorCode used when the WP_Error's own code is unregistered.
161 * @return TemplatelyError
162 */
163 public static function from_wp_error( WP_Error $error, $fallback_code = ErrorCode::SERVER_ERROR ) {
164 if ( $error instanceof self ) {
165 return $error;
166 }
167
168 $code = $error->get_error_code();
169 $data = $error->get_error_data();
170 $data = is_array( $data ) ? $data : [];
171
172 if ( ! ErrorCode::exists( $code ) ) {
173 $data['context'] = isset( $data['context'] ) && is_array( $data['context'] ) ? $data['context'] : [];
174 if ( ! empty( $code ) ) {
175 $data['context']['legacy_code'] = $code;
176 }
177 $code = $fallback_code;
178 }
179
180 return new self( $code, $error->get_error_message(), $data );
181 }
182 }
183