PluginProbe
Enable CORS / trunk
Enable CORS vtrunk
2.1.0 2.0.4 trunk 1.2.2 1.2.3 1.2.4 2.0.0 2.0.1 2.0.2 2.0.3
enable-cors / src / Helpers / Option.php

Option.php in Enable CORS trunk, at src/Helpers/Option.php

404 lines 9.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Enable\Cors\Helpers;
4
5 /*
6 |--------------------------------------------------------------------------
7 | If this file is called directly, abort.
8 |--------------------------------------------------------------------------
9 */
10 if ( ! defined( 'Enable\Cors\SLUG' ) ) {
11 exit;
12 }
13
14 use WP_Error;
15 use const Enable\Cors\SLUG;
16 use const Enable\Cors\VERSION;
17
18 /**
19 * Handle plugin option.
20 */
21 final class Option {
22
23
24 /**
25 * Plugin option key.
26 *
27 * @var string
28 */
29 private const KEY = SLUG . '-options';
30 /**
31 * Default options for the API.
32 *
33 * @var array
34 */
35 private const DEFAULT_OPTION = array(
36 'enable' => false,
37 'allow_font' => false,
38 'allow_image' => false,
39 'allow_credentials' => false,
40 'allowed_for' => array( array( 'value' => '*' ) ),
41 'allowed_methods' => array( 'GET', 'POST', 'OPTIONS' ),
42 'allowed_header' => array(),
43 );
44 /**
45 * List of allowed HTTP methods.
46 *
47 * @var array
48 */
49 private const METHODS = array(
50 'GET',
51 'POST',
52 'OPTIONS',
53 'PUT',
54 'DELETE',
55 'PATCH',
56 );
57 /**
58 * List of allowed headers.
59 *
60 * @var array
61 */
62 private const HEADERS = array(
63 'Accept',
64 'Authorization',
65 'Content-Type',
66 'Origin',
67 );
68 /**
69 * An array of allowed keys.
70 *
71 * @var array
72 */
73 private const ALLOWED = array(
74 'enable',
75 'allow_font',
76 'allow_image',
77 'allow_credentials',
78 'allowed_for',
79 'allowed_methods',
80 'allowed_header',
81 );
82 public const VKEY = SLUG . '_version';
83 /**
84 * Array of allowed headers.
85 *
86 * @var array
87 */
88 private $allowed_header;
89
90 /**
91 * Array of allowed HTTP methods.
92 *
93 * @var array
94 */
95 private $allowed_methods;
96
97 /**
98 * Array of allowed domains for CORS.
99 *
100 * @var array
101 */
102 private $allowed_for;
103
104 /**
105 * Whether to allow credentials in CORS requests.
106 *
107 * @var bool
108 */
109 private $allow_credentials;
110
111 /**
112 * Whether to allow font access in CORS requests.
113 *
114 * @var bool
115 */
116 private $allow_image;
117
118 /**
119 * Whether to allow image access in CORS requests.
120 *
121 * @var bool
122 */
123 private $allow_font;
124
125 /**
126 * Whether to enable CORS.
127 *
128 * @var bool
129 */
130 private $enable;
131
132 /**
133 * Set up options and set defaults.
134 */
135 public function __construct() {
136 $options = get_site_option( self::KEY, self::DEFAULT_OPTION );
137 if ( array_key_exists( 'allowed_for', $options ) && is_string( $options['allowed_for'] ) ) {
138 $options['allowed_for'] = array(
139 array( 'value' => $options['allowed_for'] ),
140 );
141 $this->save( $options );
142 }
143 $this->set_option( $options );
144 }
145
146 /**
147 * Save options
148 *
149 * @param array $options from request.
150 *
151 * @return bool|WP_Error
152 */
153 public function save( array $options ) {
154 $validated = $this->validate( $options );
155
156 if ( empty( $validated ) ) {
157 return new WP_Error( 'invalid', __( 'Invalid Settings!', 'enable-cors' ) );
158 }
159
160 return update_site_option( self::KEY, $validated );
161 }
162
163 /**
164 * Verify data before saving
165 *
166 * @param array $get_json_params from request.
167 *
168 * @return array of verified data
169 */
170 private function validate( array $get_json_params ): array {
171 $data = $this->extract( $get_json_params, self::ALLOWED );
172
173 array_walk(
174 $data,
175 function ( &$value, $key ) {
176 switch ( $key ) {
177 case 'allowed_for':
178 if ( ! is_array( $value ) || empty( $value ) || in_array( '*', array_column( $value, 'value' ), true ) ) {
179 $value = self::DEFAULT_OPTION['allowed_for'];
180 }
181 $value = array_map( array( $this, 'prepend_value' ), $value );
182 break;
183 case 'allowed_methods':
184 if ( ! is_array( $value ) ) {
185 $value = self::DEFAULT_OPTION['allowed_methods'];
186 }
187 $value = array_map( 'sanitize_text_field', array_filter( array_intersect( $value, self::METHODS ) ) );
188 break;
189 case 'allowed_header':
190 if ( ! is_array( $value ) ) {
191 $value = self::DEFAULT_OPTION['allowed_header'];
192 }
193 $value = array_map( 'sanitize_text_field', array_filter( array_intersect( $value, self::HEADERS ) ) );
194 break;
195 default:
196 $value = sanitize_text_field( $value );
197 $value = (bool) $value;
198 break;
199 }
200 }
201 );
202
203 return $data;
204 }
205
206 /**
207 * Extract data from array
208 *
209 * @param array $collection of data.
210 * @param array $keys to extract.
211 *
212 * @return array of extracted data
213 */
214 private function extract( array $collection, array $keys ): array {
215 return array_intersect_key( $collection, array_flip( $keys ) );
216 }
217
218 /**
219 * Set options
220 *
221 * @param array $options from request.
222 */
223 private function set_option( array $options ): void {
224 $this->enable = array_key_exists( 'enable', $options ) ? $options['enable'] : self::DEFAULT_OPTION['enable'];
225 $this->allow_font = array_key_exists( 'allow_font', $options ) ? $options['allow_font'] : self::DEFAULT_OPTION['allow_font'];
226 $this->allow_image = array_key_exists( 'allow_image', $options ) ? $options['allow_image'] : self::DEFAULT_OPTION['allow_image'];
227 $this->allow_credentials = array_key_exists( 'allow_credentials', $options ) ? $options['allow_credentials'] : self::DEFAULT_OPTION['allow_credentials'];
228 $this->allowed_for = array_key_exists( 'allowed_for', $options ) ? $options['allowed_for'] : self::DEFAULT_OPTION['allowed_for'];
229 $this->allowed_methods = array_key_exists( 'allowed_methods', $options ) ? $options['allowed_methods'] : self::DEFAULT_OPTION['allowed_methods'];
230 $this->allowed_header = array_key_exists( 'allowed_header', $options ) ? $options['allowed_header'] : self::DEFAULT_OPTION['allowed_header'];
231 }
232
233 /**
234 * Adds a default option.
235 */
236 public static function add_default(): void {
237 update_site_option( self::KEY, self::DEFAULT_OPTION );
238 update_site_option( self::VKEY, VERSION );
239 }
240
241 /**
242 * Delete options
243 */
244 public static function delete(): void {
245 delete_site_option( self::KEY );
246 delete_site_option( self::VKEY );
247 }
248
249 /**
250 * Should allow image for cors?
251 */
252 public function should_allow_image(): bool {
253 return $this->allow_image;
254 }
255
256 /**
257 * Should allow font for cors?
258 */
259 public function should_allow_font(): bool {
260 return $this->allow_font;
261 }
262
263 /**
264 * Gets the options array.
265 *
266 * @return array The options array.
267 */
268 public function get(): array {
269 return get_site_option( self::KEY, self::DEFAULT_OPTION );
270 }
271
272 /**
273 * Determines if the cors is enabled.
274 *
275 * @return bool The cors enable status.
276 */
277 public function is_enable(): bool {
278 return $this->enable;
279 }
280
281 /**
282 * Retrieves the allowed header.
283 *
284 * @return array The allowed header.
285 */
286 public function get_allowed_header(): array {
287 return $this->allowed_header;
288 }
289
290 /**
291 * Retrieves the allowed methods.
292 *
293 * @return array The list of allowed methods.
294 */
295 public function get_allowed_methods(): array {
296 return $this->allowed_methods;
297 }
298
299 /**
300 * Checks if credentials are allowed.
301 */
302 public function should_allow_credentials(): bool {
303 return $this->allow_credentials;
304 }
305
306 /**
307 * Checks whether the current origin is allowed.
308 *
309 * @return bool Returns true if the current origin is allowed, false otherwise.
310 */
311 public function is_current_origin_allowed(): bool {
312 $websites = array_column( $this->allowed_for, 'value' );
313
314 return in_array( rtrim( get_http_origin(), '/' ), $websites, true );
315 }
316
317 /**
318 * Check if the method is allowed.
319 */
320 public function has_methods(): bool {
321 return ! empty( $this->allowed_methods );
322 }
323
324 /**
325 * Checks if the object has a header.
326 *
327 * @return bool Returns true if the object has a header, false otherwise.
328 */
329 public function has_header(): bool {
330 return ! empty( $this->allowed_header );
331 }
332
333 /**
334 * Check if the method is allowed.
335 *
336 * @return bool Returns true if the method is allowed, false otherwise.
337 */
338 public function is_method_allowed(): bool {
339 $method = sanitize_text_field( wp_unslash( $_SERVER['REQUEST_METHOD'] ?? '' ) );
340
341 return ! empty( $method ) && in_array( $method, $this->allowed_methods, true );
342 }
343
344 /**
345 * Checks if the array of allowed websites contains a wildcard value.
346 */
347 public function has_wildcard(): bool {
348 $websites = array_column( $this->allowed_for, 'value' );
349
350 return 1 === count( $websites ) && in_array( '*', $websites, true );
351 }
352
353 /**
354 * Retrieves an array of domains from the 'allowed_for' property by parsing the 'value' field of each website.
355 *
356 * @return array An array of domains extracted from the 'value' field of each website in the 'allowed_for' property.
357 */
358 public function get_domains(): array {
359 return array_map(
360 static function ( $website ) {
361 return wp_parse_url( $website['value'], PHP_URL_HOST );
362 },
363 $this->allowed_for
364 );
365 }
366
367 /**
368 * Updates the version number in the options table.
369 *
370 * @return void
371 */
372 public function update_version() {
373 update_site_option( self::VKEY, VERSION );
374 }
375
376 /**
377 * Prepend value to url
378 *
379 * @param array $url from allowed_for.
380 *
381 * @return array of formatted url
382 */
383 private function prepend_value( array $url ): array {
384 if ( ! array_key_exists( 'value', $url ) ) {
385 return $url;
386 }
387 if ( '*' === $url['value'] ) {
388 return $url;
389 }
390 $url['value'] = sanitize_url( rtrim( $url['value'], '/' ) );
391
392 return $url;
393 }
394
395 /**
396 * Retrieves the version number from the options table.
397 *
398 * @return string The version number stored in the options table associated with the VKEY.
399 */
400 public function get_version(): string {
401 return get_site_option( self::VKEY, '1.0.0' );
402 }
403 }
404