1<?php
2/**
3 * WordPress Error API.
4 *
5 * @package WordPress
6 */
7
8/**
9 * WordPress Error class.
10 *
11 * Container for checking for WordPress errors and error messages. Return
12 * WP_Error and use is_wp_error() to check if this class is returned. Many
13 * core WordPress functions pass this class in the event of an error and
14 * if not handled properly will result in code errors.
15 *
16 * @since 2.1.0
17 */
18#[AllowDynamicProperties]
19class WP_Error {
20 /**
21 * Stores the list of errors.
22 *
23 * @since 2.1.0
24 * @var array
25 */
26 public $errors = array();
27
28 /**
29 * Stores the most recently added data for each error code.
30 *
31 * @since 2.1.0
32 * @var array
33 */
34 public $error_data = array();
35
36 /**
37 * Stores previously added data added for error codes, oldest-to-newest by code.
38 *
39 * @since 5.6.0
40 * @var array[]
41 */
42 protected $additional_data = array();
43
44 /**
45 * Initializes the error.
46 *
47 * If `$code` is empty, the other parameters will be ignored.
48 * When `$code` is not empty, `$message` will be used even if
49 * it is empty. The `$data` parameter will be used only if it
50 * is not empty.
51 *
52 * Though the class is constructed with a single error code and
53 * message, multiple codes can be added using the `add()` method.
54 *
55 * @since 2.1.0
56 *
57 * @param string|int $code Error code.
58 * @param string $message Error message.
59 * @param mixed $data Optional. Error data. Default empty string.
60 */
61 public function __construct( $code = '', $message = '', $data = '' ) {
62 if ( empty( $code ) ) {
63 return;
64 }
65
66 $this->add( $code, $message, $data );
67 }
68
69 /**
70 * Retrieves all error codes.
71 *
72 * @since 2.1.0
73 *
74 * @return array List of error codes, if available.
75 */
76 public function get_error_codes() {
77 if ( ! $this->has_errors() ) {
78 return array();
79 }
80
81 return array_keys( $this->errors );
82 }
83
84 /**
85 * Retrieves the first error code available.
86 *
87 * @since 2.1.0
88 *
89 * @return string|int Empty string, if no error codes.
90 */
91 public function get_error_code() {
92 $codes = $this->get_error_codes();
93
94 if ( empty( $codes ) ) {
95 return '';
96 }
97
98 return $codes[0];
99 }
100
101 /**
102 * Retrieves all error messages, or the error messages for the given error code.
103 *
104 * @since 2.1.0
105 *
106 * @param string|int $code Optional. Error code to retrieve the messages for.
107 * Default empty string.
108 * @return string[] Error strings on success, or empty array if there are none.
109 */
110 public function get_error_messages( $code = '' ) {
111 // Return all messages if no code specified.
112 if ( empty( $code ) ) {
113 $all_messages = array();
114 foreach ( (array) $this->errors as $code => $messages ) {
115 $all_messages = array_merge( $all_messages, $messages );
116 }
117
118 return $all_messages;
119 }
120
121 if ( isset( $this->errors[ $code ] ) ) {
122 return $this->errors[ $code ];
123 } else {
124 return array();
125 }
126 }
127
128 /**
129 * Gets a single error message.
130 *
131 * This will get the first message available for the code. If no code is
132 * given then the first code available will be used.
133 *
134 * @since 2.1.0
135 *
136 * @param string|int $code Optional. Error code to retrieve the message for.
137 * Default empty string.
138 * @return string The error message.
139 */
140 public function get_error_message( $code = '' ) {
141 if ( empty( $code ) ) {
142 $code = $this->get_error_code();
143 }
144 $messages = $this->get_error_messages( $code );
145 if ( empty( $messages ) ) {
146 return '';
147 }
148 return $messages[0];
149 }
150
151 /**
152 * Retrieves the most recently added error data for an error code.
153 *
154 * @since 2.1.0
155 *
156 * @param string|int $code Optional. Error code. Default empty string.
157 * @return mixed Error data, if it exists.
158 */
159 public function get_error_data( $code = '' ) {
160 if ( empty( $code ) ) {
161 $code = $this->get_error_code();
162 }
163
164 if ( isset( $this->error_data[ $code ] ) ) {
165 return $this->error_data[ $code ];
166 }
167 }
168
169 /**
170 * Verifies if the instance contains errors.
171 *
172 * @since 5.1.0
173 *
174 * @return bool If the instance contains errors.
175 */
176 public function has_errors() {
177 if ( ! empty( $this->errors ) ) {
178 return true;
179 }
180 return false;
181 }
182
183 /**
184 * Adds an error or appends an additional message to an existing error.
185 *
186 * @since 2.1.0
187 *
188 * @param string|int $code Error code.
189 * @param string $message Error message.
190 * @param mixed $data Optional. Error data. Default empty string.
191 */
192 public function add( $code, $message, $data = '' ) {
193 $this->errors[ $code ][] = $message;
194
195 if ( ! empty( $data ) ) {
196 $this->add_data( $data, $code );
197 }
198
199 /**
200 * Fires when an error is added to a WP_Error object.
201 *
202 * @since 5.6.0
203 *
204 * @param string|int $code Error code.
205 * @param string $message Error message.
206 * @param mixed $data Error data. Might be empty.
207 * @param WP_Error $wp_error The WP_Error object.
208 */
209 do_action( 'wp_error_added', $code, $message, $data, $this );
210 }
211
212 /**
213 * Adds data to an error with the given code.
214 *
215 * @since 2.1.0
216 * @since 5.6.0 Errors can now contain more than one item of error data. {@see WP_Error::$additional_data}.
217 *
218 * @param mixed $data Error data.
219 * @param string|int $code Error code.
220 */
221 public function add_data( $data, $code = '' ) {
222 if ( empty( $code ) ) {
223 $code = $this->get_error_code();
224 }
225
226 if ( isset( $this->error_data[ $code ] ) ) {
227 $this->additional_data[ $code ][] = $this->error_data[ $code ];
228 }
229
230 $this->error_data[ $code ] = $data;
231 }
232
233 /**
234 * Retrieves all error data for an error code in the order in which the data was added.
235 *
236 * @since 5.6.0
237 *
238 * @param string|int $code Error code.
239 * @return mixed[] Array of error data, if it exists.
240 */
241 public function get_all_error_data( $code = '' ) {
242 if ( empty( $code ) ) {
243 $code = $this->get_error_code();
244 }
245
246 $data = array();
247
248 if ( isset( $this->additional_data[ $code ] ) ) {
249 $data = $this->additional_data[ $code ];
250 }
251
252 if ( isset( $this->error_data[ $code ] ) ) {
253 $data[] = $this->error_data[ $code ];
254 }
255
256 return $data;
257 }
258
259 /**
260 * Removes the specified error.
261 *
262 * This function removes all error messages associated with the specified
263 * error code, along with any error data for that code.
264 *
265 * @since 4.1.0
266 *
267 * @param string|int $code Error code.
268 */
269 public function remove( $code ) {
270 unset( $this->errors[ $code ] );
271 unset( $this->error_data[ $code ] );
272 unset( $this->additional_data[ $code ] );
273 }
274
275 /**
276 * Merges the errors in the given error object into this one.
277 *
278 * @since 5.6.0
279 *
280 * @param WP_Error $error Error object to merge.
281 */
282 public function merge_from( WP_Error $error ) {
283 static::copy_errors( $error, $this );
284 }
285
286 /**
287 * Exports the errors in this object into the given one.
288 *
289 * @since 5.6.0
290 *
291 * @param WP_Error $error Error object to export into.
292 */
293 public function export_to( WP_Error $error ) {
294 static::copy_errors( $this, $error );
295 }
296
297 /**
298 * Copies errors from one WP_Error instance to another.
299 *
300 * @since 5.6.0
301 *
302 * @param WP_Error $from The WP_Error to copy from.
303 * @param WP_Error $to The WP_Error to copy to.
304 */
305 protected static function copy_errors( WP_Error $from, WP_Error $to ) {
306 foreach ( $from->get_error_codes() as $code ) {
307 foreach ( $from->get_error_messages( $code ) as $error_message ) {
308 $to->add( $code, $error_message );
309 }
310
311 foreach ( $from->get_all_error_data( $code ) as $data ) {
312 $to->add_data( $data, $code );
313 }
314 }
315 }
316}
317