PluginProbe
Redirection / 5.8.1
Redirection v5.8.1
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.8.1, at models/options.php

450 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 } else {
307 $options['monitor_post'] = 0;
308 }
309 }
310 }
311
312 if ( isset( $settings['auto_target'] ) && is_string( $settings['auto_target'] ) ) {
313 $options['auto_target'] = sanitize_text_field( $settings['auto_target'] );
314 }
315
316 if ( isset( $settings['last_group_id'] ) ) {
317 $options['last_group_id'] = max( 0, intval( $settings['last_group_id'], 10 ) );
318
319 if ( Red_Group::get( $options['last_group_id'] ) === false ) {
320 $groups = Red_Group::get_all();
321 $options['last_group_id'] = count( $groups ) > 0 ? $groups[0]['id'] : 0;
322 }
323 }
324
325 if ( isset( $settings['token'] ) && is_string( $settings['token'] ) ) {
326 $options['token'] = sanitize_text_field( $settings['token'] );
327 }
328
329 if ( isset( $settings['token'] ) && trim( $options['token'] ) === '' ) {
330 $options['token'] = md5( uniqid() );
331 }
332
333 // Boolean settings
334 foreach ( [ 'support', 'https', 'log_external', 'log_header', 'track_hits' ] as $name ) {
335 if ( isset( $settings[ $name ] ) ) {
336 $options[ $name ] = $settings[ $name ] ? true : false;
337 }
338 }
339
340 if ( isset( $settings['expire_redirect'] ) ) {
341 $options['expire_redirect'] = max( -1, min( intval( $settings['expire_redirect'], 10 ), 60 ) );
342 }
343
344 if ( isset( $settings['expire_404'] ) ) {
345 $options['expire_404'] = max( -1, min( intval( $settings['expire_404'], 10 ), 60 ) );
346 }
347
348 if ( isset( $settings['ip_logging'] ) ) {
349 $options['ip_logging'] = max( 0, min( 2, intval( $settings['ip_logging'], 10 ) ) );
350 }
351
352 if ( isset( $settings['redirect_cache'] ) ) {
353 $options['redirect_cache'] = intval( $settings['redirect_cache'], 10 );
354
355 if ( ! in_array( $options['redirect_cache'], array( -1, 0, 1, 24, 24 * 7 ), true ) ) {
356 $options['redirect_cache'] = 1;
357 }
358 }
359
360 if ( isset( $settings['location'] ) && ( ! isset( $options['location'] ) || $options['location'] !== $settings['location'] ) ) {
361 $module = Red_Module::get( 2 );
362
363 if ( $module !== false ) {
364 $options['modules'][2] = $module->update( $settings );
365 }
366 }
367
368 if ( ! empty( $options['monitor_post'] ) && count( $options['monitor_types'] ) === 0 ) {
369 // If we have a monitor_post set, but no types, then blank everything
370 $options['monitor_post'] = 0;
371 $options['associated_redirect'] = '';
372 }
373
374 if ( isset( $settings['plugin_update'] ) && in_array( $settings['plugin_update'], [ 'prompt', 'admin' ], true ) ) {
375 $options['plugin_update'] = sanitize_text_field( $settings['plugin_update'] );
376 }
377
378 $flags = new Red_Source_Flags();
379 $flags_present = [];
380
381 foreach ( array_keys( $flags->get_json() ) as $flag ) {
382 if ( isset( $settings[ $flag ] ) ) {
383 $flags_present[ $flag ] = $settings[ $flag ];
384 }
385 }
386
387 if ( count( $flags_present ) > 0 ) {
388 $flags->set_flags( $flags_present );
389 $options = array_merge( $options, $flags->get_json() );
390 }
391
392 if ( isset( $settings['headers'] ) ) {
393 $headers = new Red_Http_Headers( $settings['headers'] );
394 $options['headers'] = $headers->get_json();
395 }
396
397 if ( isset( $settings['aliases'] ) && is_array( $settings['aliases'] ) ) {
398 $options['aliases'] = array_map( 'sanitize_text_field', $settings['aliases'] );
399 $options['aliases'] = array_values( array_filter( array_map( 'red_parse_domain_only', $settings['aliases'] ) ) );
400 $options['aliases'] = array_slice( $options['aliases'], 0, 20 ); // Max 20
401 }
402
403 if ( isset( $settings['permalinks'] ) && is_array( $settings['permalinks'] ) ) {
404 $options['permalinks'] = array_map(
405 function ( $permalink ) {
406 return sanitize_option( 'permalink_structure', $permalink );
407 },
408 $settings['permalinks']
409 );
410 $options['permalinks'] = array_values( array_filter( array_map( 'trim', $options['permalinks'] ) ) );
411 $options['permalinks'] = array_slice( $options['permalinks'], 0, 10 ); // Max 10
412 }
413
414 if ( isset( $settings['preferred_domain'] ) && in_array( $settings['preferred_domain'], [ '', 'www', 'nowww' ], true ) ) {
415 $options['preferred_domain'] = sanitize_text_field( $settings['preferred_domain'] );
416 }
417
418 if ( isset( $settings['relocate'] ) && is_string( $settings['relocate'] ) ) {
419 $options['relocate'] = red_parse_domain_path( sanitize_text_field( $settings['relocate'] ) );
420
421 if ( strlen( $options['relocate'] ) > 0 ) {
422 $options['preferred_domain'] = '';
423 $options['aliases'] = [];
424 $options['https'] = false;
425 }
426 }
427
428 if ( isset( $settings['cache_key'] ) ) {
429 $key = intval( $settings['cache_key'], 10 );
430
431 if ( $settings['cache_key'] === true ) {
432 $key = time();
433 } elseif ( $settings['cache_key'] === false ) {
434 $key = 0;
435 }
436
437 $options['cache_key'] = $key;
438 }
439
440 if ( isset( $settings['update_notice'] ) ) {
441 $major_version = explode( '-', REDIRECTION_VERSION )[0]; // Remove any beta suffix
442 $major_version = implode( '.', array_slice( explode( '.', $major_version ), 0, 2 ) );
443 $options['update_notice'] = $major_version;
444 }
445
446 /** @var RedirectionOptions $options */
447 return $options;
448 }
449 }
450