PluginProbe
Redirection / 5.6.0
Redirection v5.6.0
5.10.0 5.9.0 5.8.1 5.8.0 3.7.2 3.7.3 4.0 4.0.1 4.1 4.1.1 4.2 4.2.1 4.2.2 4.2.3 4.3 4.3.1 4.3.2 4.3.3 4.4 4.4.1 4.4.2 4.5 4.5.1 4.6.2 4.7.1 All 130 releases
redirection / models / options.php

options.php in Redirection 5.6.0, at models/options.php

448 lines 13.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Redirection options manager - pure static utility class.
5 *
6 * Usage: Red_Options::get() returns a fully typed array.
7 *
8 * @phpstan-type RedirectionOptions array{
9 * support: bool,
10 * token: string,
11 * monitor_post: int,
12 * monitor_types: array<string>,
13 * associated_redirect: string,
14 * auto_target: string,
15 * expire_redirect: int,
16 * expire_404: int,
17 * log_external: bool,
18 * log_header: bool,
19 * track_hits: bool,
20 * modules: array<int, mixed>,
21 * redirect_cache: int,
22 * ip_logging: int,
23 * ip_headers: array<string>,
24 * ip_proxy: array<string>,
25 * last_group_id: int,
26 * rest_api: int,
27 * https: bool,
28 * headers: array<mixed>,
29 * database: string,
30 * relocate: string,
31 * preferred_domain: string,
32 * aliases: array<string>,
33 * permalinks: array<mixed>,
34 * cache_key: int,
35 * plugin_update: string,
36 * update_notice: int,
37 * flag_query: 'ignore'|'exact'|'pass'|'exactorder',
38 * flag_case: bool,
39 * flag_trailing: bool,
40 * flag_regex: bool,
41 * database_stage?: array{stage?: string|false, stages?: array<mixed>, status?: string|false},
42 * location?: string
43 * }
44 */
45 class Red_Options {
46 /**
47 * Options DB key. Previously defined by REDIRECTION_OPTION.
48 */
49 public const OPTION_KEY = 'redirection_options';
50
51 /**
52 * REST API location constants. Previously REDIRECTION_API_JSON*, now centralized here.
53 */
54 public const API_JSON = 0;
55 public const API_JSON_INDEX = 1;
56 public const API_JSON_RELATIVE = 3;
57
58 /**
59 * In-memory cache for build_options result.
60 *
61 * @var RedirectionOptions|null
62 */
63 private static $options_cache = null;
64
65 /**
66 * Prevent instantiation - this is a static utility class.
67 */
68 private function __construct() {
69 }
70
71 /**
72 * Load current options and return as a typed array.
73 *
74 * @phpstan-return RedirectionOptions
75 * @return array
76 */
77 public static function get(): array {
78 return self::build_options();
79 }
80
81 /**
82 * Reset the options cache.
83 * @return void
84 */
85 public static function reset() {
86 self::$options_cache = null;
87 }
88
89 /**
90 * Build Redirection options (array form).
91 * Mirrors legacy red_get_options() behavior.
92 *
93 * @phpstan-return RedirectionOptions
94 * @return array
95 */
96 private static function build_options(): array {
97 // Return cached result if available
98 if ( self::$options_cache !== null ) {
99 return self::$options_cache;
100 }
101
102 $options = get_option( self::OPTION_KEY );
103 $fresh_install = false;
104
105 if ( $options === false ) {
106 $fresh_install = true;
107 }
108
109 if ( ! is_array( $options ) ) {
110 $options = [];
111 }
112
113 if ( red_is_disabled() ) {
114 $options['https'] = false;
115 }
116
117 $defaults = self::get_default_options();
118
119 foreach ( $defaults as $key => $value ) {
120 if ( ! isset( $options[ $key ] ) ) {
121 $options[ $key ] = $value;
122 }
123 }
124
125 if ( $fresh_install ) {
126 // Default flags for new installs - ignore case and trailing slashes
127 $options['flag_case'] = true;
128 $options['flag_trailing'] = true;
129 }
130
131 // Back-compat. If monitor_post is set without types then it's from an older Redirection
132 if ( $options['monitor_post'] > 0 && count( $options['monitor_types'] ) === 0 ) {
133 $options['monitor_types'] = [ 'post' ];
134 }
135
136 // Remove old options not in get_default_options()
137 foreach ( array_keys( $options ) as $key ) {
138 if ( ! isset( $defaults[ $key ] ) && $key !== 'database_stage' ) {
139 unset( $options[ $key ] );
140 }
141 }
142
143 // Back-compat fix
144 if ( $options['rest_api'] === false || ! in_array( $options['rest_api'], [ self::API_JSON, self::API_JSON_INDEX, self::API_JSON_RELATIVE ], true ) ) {
145 $options['rest_api'] = self::API_JSON;
146 }
147
148 if ( isset( $options['modules'] ) && isset( $options['modules']['2'] ) && isset( $options['modules']['2']['location'] ) ) {
149 $options['location'] = $options['modules']['2']['location'];
150 }
151
152 /** @var RedirectionOptions $options */
153 // Cache the result before returning
154 self::$options_cache = $options;
155 return $options;
156 }
157
158 /**
159 * Get default options. Contains all valid options.
160 *
161 * @phpstan-return RedirectionOptions
162 * @return array<string, mixed>
163 */
164 public static function get_default_options() {
165 $flags = new Red_Source_Flags();
166 $defaults = [
167 'support' => false,
168 'token' => md5( uniqid() ),
169 'monitor_post' => 0,
170 'monitor_types' => [],
171 'associated_redirect' => '',
172 'auto_target' => '',
173 'expire_redirect' => 7,
174 'expire_404' => 7,
175 'log_external' => false,
176 'log_header' => false,
177 'track_hits' => true,
178 'modules' => [],
179 'redirect_cache' => 1,
180 'ip_logging' => 0,
181 'ip_headers' => [],
182 'ip_proxy' => [],
183 'last_group_id' => 0,
184 'rest_api' => self::API_JSON,
185 'https' => false,
186 'headers' => [],
187 'database' => '',
188 'relocate' => '',
189 'preferred_domain' => '',
190 'aliases' => [],
191 'permalinks' => [],
192 'cache_key' => 0,
193 'plugin_update' => 'prompt',
194 'update_notice' => 0,
195 ];
196
197 $defaults = array_merge( $defaults, $flags->get_json() );
198
199 return apply_filters( 'red_default_options', $defaults );
200 }
201
202 /**
203 * Persist option values using the same sanitization/merge rules as legacy red_set_options.
204 * This is a static method that saves options and returns the updated array.
205 *
206 * @param array<string, mixed> $settings Partial settings to apply.
207 * @phpstan-return RedirectionOptions
208 * @return array
209 */
210 public static function save( array $settings ): array {
211 // Clear the cache before applying settings
212 self::$options_cache = null;
213
214 $options = self::apply_settings( $settings );
215 update_option( self::OPTION_KEY, apply_filters( 'redirection_save_options', $options ) );
216
217 // Clear the cache after saving to ensure fresh data on next get()
218 self::$options_cache = null;
219
220 /** @var RedirectionOptions $options */
221 return $options;
222 }
223
224 /**
225 * Apply settings and return the updated options array without saving.
226 *
227 * @param array<string, mixed> $settings Partial settings to apply.
228 * @phpstan-return RedirectionOptions
229 * @return array
230 */
231 private static function apply_settings( array $settings ): array {
232 $options = self::build_options();
233 $monitor_types = [];
234
235 if ( isset( $settings['database'] ) ) {
236 $options['database'] = sanitize_text_field( $settings['database'] );
237 }
238
239 if ( array_key_exists( 'database_stage', $settings ) ) {
240 if ( $settings['database_stage'] === false ) {
241 unset( $options['database_stage'] );
242 } else {
243 $options['database_stage'] = $settings['database_stage'];
244 }
245 }
246
247 if ( isset( $settings['ip_proxy'] ) && is_array( $settings['ip_proxy'] ) ) {
248 $options['ip_proxy'] = array_map(
249 function ( $ip ) {
250 $ip = new Redirection_IP( $ip );
251 return $ip->get();
252 },
253 $settings['ip_proxy']
254 );
255
256 $options['ip_proxy'] = array_values( array_filter( $options['ip_proxy'] ) );
257 }
258
259 if ( isset( $settings['ip_headers'] ) && is_array( $settings['ip_headers'] ) ) {
260 $available = Redirection_Request::get_ip_headers();
261 $options['ip_headers'] = array_filter(
262 $settings['ip_headers'],
263 function ( $header ) use ( $available ) {
264 return in_array( $header, $available, true );
265 }
266 );
267 $options['ip_headers'] = array_values( $options['ip_headers'] );
268 }
269
270 if ( isset( $settings['rest_api'] ) && in_array( intval( $settings['rest_api'], 10 ), array( 0, 1, 2, 3, 4 ), true ) ) {
271 $options['rest_api'] = intval( $settings['rest_api'], 10 );
272 }
273
274 if ( isset( $settings['monitor_types'] ) && is_array( $settings['monitor_types'] ) ) {
275 $allowed = red_get_post_types( false );
276
277 foreach ( $settings['monitor_types'] as $type ) {
278 if ( in_array( $type, $allowed, true ) ) {
279 $monitor_types[] = $type;
280 }
281 }
282
283 $options['monitor_types'] = $monitor_types;
284 }
285
286 if ( isset( $settings['associated_redirect'] ) && is_string( $settings['associated_redirect'] ) ) {
287 $options['associated_redirect'] = '';
288
289 if ( strlen( $settings['associated_redirect'] ) > 0 ) {
290 $sanitizer = new Red_Item_Sanitize();
291 $options['associated_redirect'] = trim( $sanitizer->sanitize_url( $settings['associated_redirect'] ) );
292 }
293 }
294
295 if ( isset( $settings['monitor_types'] ) && count( $monitor_types ) === 0 ) {
296 $options['monitor_post'] = 0;
297 $options['associated_redirect'] = '';
298 } elseif ( isset( $settings['monitor_post'] ) ) {
299 $options['monitor_post'] = max( 0, intval( $settings['monitor_post'], 10 ) );
300
301 if ( Red_Group::get( $options['monitor_post'] ) === false && $options['monitor_post'] !== 0 ) {
302 $groups = Red_Group::get_all();
303
304 if ( count( $groups ) > 0 ) {
305 $options['monitor_post'] = $groups[0]['id'];
306 }
307 }
308 }
309
310 if ( isset( $settings['auto_target'] ) && is_string( $settings['auto_target'] ) ) {
311 $options['auto_target'] = sanitize_text_field( $settings['auto_target'] );
312 }
313
314 if ( isset( $settings['last_group_id'] ) ) {
315 $options['last_group_id'] = max( 0, intval( $settings['last_group_id'], 10 ) );
316
317 if ( Red_Group::get( $options['last_group_id'] ) === false ) {
318 $groups = Red_Group::get_all();
319 $options['last_group_id'] = $groups[0]['id'];
320 }
321 }
322
323 if ( isset( $settings['token'] ) && is_string( $settings['token'] ) ) {
324 $options['token'] = sanitize_text_field( $settings['token'] );
325 }
326
327 if ( isset( $settings['token'] ) && trim( $options['token'] ) === '' ) {
328 $options['token'] = md5( uniqid() );
329 }
330
331 // Boolean settings
332 foreach ( [ 'support', 'https', 'log_external', 'log_header', 'track_hits' ] as $name ) {
333 if ( isset( $settings[ $name ] ) ) {
334 $options[ $name ] = $settings[ $name ] ? true : false;
335 }
336 }
337
338 if ( isset( $settings['expire_redirect'] ) ) {
339 $options['expire_redirect'] = max( -1, min( intval( $settings['expire_redirect'], 10 ), 60 ) );
340 }
341
342 if ( isset( $settings['expire_404'] ) ) {
343 $options['expire_404'] = max( -1, min( intval( $settings['expire_404'], 10 ), 60 ) );
344 }
345
346 if ( isset( $settings['ip_logging'] ) ) {
347 $options['ip_logging'] = max( 0, min( 2, intval( $settings['ip_logging'], 10 ) ) );
348 }
349
350 if ( isset( $settings['redirect_cache'] ) ) {
351 $options['redirect_cache'] = intval( $settings['redirect_cache'], 10 );
352
353 if ( ! in_array( $options['redirect_cache'], array( -1, 0, 1, 24, 24 * 7 ), true ) ) {
354 $options['redirect_cache'] = 1;
355 }
356 }
357
358 if ( isset( $settings['location'] ) && ( ! isset( $options['location'] ) || $options['location'] !== $settings['location'] ) ) {
359 $module = Red_Module::get( 2 );
360
361 if ( $module !== false ) {
362 $options['modules'][2] = $module->update( $settings );
363 }
364 }
365
366 if ( ! empty( $options['monitor_post'] ) && count( $options['monitor_types'] ) === 0 ) {
367 // If we have a monitor_post set, but no types, then blank everything
368 $options['monitor_post'] = 0;
369 $options['associated_redirect'] = '';
370 }
371
372 if ( isset( $settings['plugin_update'] ) && in_array( $settings['plugin_update'], [ 'prompt', 'admin' ], true ) ) {
373 $options['plugin_update'] = sanitize_text_field( $settings['plugin_update'] );
374 }
375
376 $flags = new Red_Source_Flags();
377 $flags_present = [];
378
379 foreach ( array_keys( $flags->get_json() ) as $flag ) {
380 if ( isset( $settings[ $flag ] ) ) {
381 $flags_present[ $flag ] = $settings[ $flag ];
382 }
383 }
384
385 if ( count( $flags_present ) > 0 ) {
386 $flags->set_flags( $flags_present );
387 $options = array_merge( $options, $flags->get_json() );
388 }
389
390 if ( isset( $settings['headers'] ) ) {
391 $headers = new Red_Http_Headers( $settings['headers'] );
392 $options['headers'] = $headers->get_json();
393 }
394
395 if ( isset( $settings['aliases'] ) && is_array( $settings['aliases'] ) ) {
396 $options['aliases'] = array_map( 'sanitize_text_field', $settings['aliases'] );
397 $options['aliases'] = array_values( array_filter( array_map( 'red_parse_domain_only', $settings['aliases'] ) ) );
398 $options['aliases'] = array_slice( $options['aliases'], 0, 20 ); // Max 20
399 }
400
401 if ( isset( $settings['permalinks'] ) && is_array( $settings['permalinks'] ) ) {
402 $options['permalinks'] = array_map(
403 function ( $permalink ) {
404 return sanitize_option( 'permalink_structure', $permalink );
405 },
406 $settings['permalinks']
407 );
408 $options['permalinks'] = array_values( array_filter( array_map( 'trim', $options['permalinks'] ) ) );
409 $options['permalinks'] = array_slice( $options['permalinks'], 0, 10 ); // Max 10
410 }
411
412 if ( isset( $settings['preferred_domain'] ) && in_array( $settings['preferred_domain'], [ '', 'www', 'nowww' ], true ) ) {
413 $options['preferred_domain'] = sanitize_text_field( $settings['preferred_domain'] );
414 }
415
416 if ( isset( $settings['relocate'] ) && is_string( $settings['relocate'] ) ) {
417 $options['relocate'] = red_parse_domain_path( sanitize_text_field( $settings['relocate'] ) );
418
419 if ( strlen( $options['relocate'] ) > 0 ) {
420 $options['preferred_domain'] = '';
421 $options['aliases'] = [];
422 $options['https'] = false;
423 }
424 }
425
426 if ( isset( $settings['cache_key'] ) ) {
427 $key = intval( $settings['cache_key'], 10 );
428
429 if ( $settings['cache_key'] === true ) {
430 $key = time();
431 } elseif ( $settings['cache_key'] === false ) {
432 $key = 0;
433 }
434
435 $options['cache_key'] = $key;
436 }
437
438 if ( isset( $settings['update_notice'] ) ) {
439 $major_version = explode( '-', REDIRECTION_VERSION )[0]; // Remove any beta suffix
440 $major_version = implode( '.', array_slice( explode( '.', $major_version ), 0, 2 ) );
441 $options['update_notice'] = $major_version;
442 }
443
444 /** @var RedirectionOptions $options */
445 return $options;
446 }
447 }
448