| 1 |
<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName -- File naming is acceptable for this plugin structure |
| 2 |
|
| 3 |
/** |
| 4 |
* Data sanitization class. |
| 5 |
* |
| 6 |
* This class provides methods for sanitizing and validating data |
| 7 |
* according to specified rules and contexts. |
| 8 |
* |
| 9 |
* @package WP_Analytify |
| 10 |
* @since 1.0.0 |
| 11 |
*/ |
| 12 |
class WPANALYTIFY_Sanitize { |
| 13 |
|
| 14 |
/** |
| 15 |
* Sanitize and validate data. |
| 16 |
* |
| 17 |
* @param array<string, mixed> $data The data to be sanitized. |
| 18 |
* @param array<string, mixed> $key_rules The keys in the data and the sanitization rule(s) to apply for each key. |
| 19 |
* @param string $context Additional context data for messages. |
| 20 |
* @return mixed The sanitized data, the data if no key rules supplied or false if an unrecognized rule supplied. |
| 21 |
*/ |
| 22 |
public static function sanitize_data( $data, $key_rules, $context ) { |
| 23 |
if ( empty( $data ) || empty( $key_rules ) ) { |
| 24 |
return $data; |
| 25 |
} |
| 26 |
|
| 27 |
return self::_sanitize_data( $data, $key_rules, $context ); |
| 28 |
} |
| 29 |
|
| 30 |
/** |
| 31 |
* Internal sanitization method. |
| 32 |
* |
| 33 |
* @param array<string, mixed> $data The data to be sanitized. |
| 34 |
* @param array<string, mixed> $key_rules The keys in the data and the sanitization rule(s) to apply for each key. |
| 35 |
* @param string $context Additional context data for messages. |
| 36 |
* @param int $recursion_level The current recursion level to prevent infinite loops. |
| 37 |
* @return mixed The sanitized data. |
| 38 |
*/ |
| 39 |
private static function _sanitize_data( $data, $key_rules, $context, $recursion_level = 0 ) { // phpcs:ignore PSR2.Methods.MethodDeclaration.Underscore -- Underscore prefix is intentional for internal method |
| 40 |
if ( empty( $data ) || empty( $key_rules ) ) { |
| 41 |
return $data; |
| 42 |
} |
| 43 |
|
| 44 |
if ( 0 === $recursion_level && is_array( $data ) ) { |
| 45 |
// We always expect associative arrays. |
| 46 |
if ( ! is_array( $key_rules ) ) { |
| 47 |
// translators: Array Error. |
| 48 |
wp_die( sprintf( esc_html__( '%1$s was not expecting data to be an array.', 'wp-analytify' ), esc_html( $context ) ) ); |
| 49 |
} |
| 50 |
foreach ( $data as $key => $value ) { |
| 51 |
// If a key does not have a rule it's not ours and can be removed. |
| 52 |
// We should not fail if there is extra data as plugins like Polylang add their own data to each ajax request. |
| 53 |
if ( ! array_key_exists( $key, $key_rules ) ) { |
| 54 |
unset( $data[ $key ] ); |
| 55 |
continue; |
| 56 |
} |
| 57 |
$data[ $key ] = self::_sanitize_data( $value, $key_rules[ $key ], $context, ( $recursion_level + 1 ) ); |
| 58 |
} |
| 59 |
} elseif ( is_array( $key_rules ) ) { |
| 60 |
foreach ( $key_rules as $rule ) { |
| 61 |
$data = self::_sanitize_data( $data, $rule, $context, ( $recursion_level + 1 ) ); |
| 62 |
} |
| 63 |
} elseif ( 'array' === $key_rules ) { |
| 64 |
// Neither $data or $key_rules are a first level array so can be analysed. |
| 65 |
if ( ! is_array( $data ) ) { |
| 66 |
// translators: Array Error. |
| 67 |
wp_die( sprintf( esc_html__( '%1$s was expecting an array but got something else: "%2$s"', 'wp-analytify' ), esc_html( $context ), esc_html( $data ) ) ); |
| 68 |
} |
| 69 |
} elseif ( 'string' === $key_rules ) { |
| 70 |
if ( ! is_string( $data ) ) { |
| 71 |
// translators: String Error. |
| 72 |
wp_die( sprintf( esc_html__( '%1$s was expecting a string but got something else: "%2$s"', 'wp-analytify' ), esc_html( $context ), is_array( $data ) ? 'array' : esc_html( $data ) ) ); |
| 73 |
} |
| 74 |
} elseif ( 'key' === $key_rules ) { |
| 75 |
if ( ! is_string( $data ) ) { |
| 76 |
// translators: Key Error. |
| 77 |
wp_die( sprintf( esc_html__( '%1$s was expecting a string for key sanitization but got something else: "%2$s"', 'wp-analytify' ), esc_html( $context ), is_array( $data ) ? 'array' : esc_html( $data ) ) ); |
| 78 |
} |
| 79 |
$key_name = sanitize_key( $data ); |
| 80 |
if ( $key_name !== $data ) { |
| 81 |
// translators: Key Error. |
| 82 |
wp_die( sprintf( esc_html__( '%1$s was expecting a valid key but got something else: "%2$s"', 'wp-analytify' ), esc_html( $context ), esc_html( $data ) ) ); |
| 83 |
} |
| 84 |
$data = $key_name; |
| 85 |
} elseif ( 'text' === $key_rules ) { |
| 86 |
if ( ! is_string( $data ) ) { |
| 87 |
// translators: Text Error. |
| 88 |
wp_die( sprintf( esc_html__( '%1$s was expecting a string for text sanitization but got something else: "%2$s"', 'wp-analytify' ), esc_html( $context ), is_array( $data ) ? 'array' : esc_html( $data ) ) ); |
| 89 |
} |
| 90 |
$text = sanitize_text_field( $data ); |
| 91 |
if ( $text !== $data ) { |
| 92 |
// translators: Text Error. |
| 93 |
wp_die( sprintf( esc_html__( '%1$s was expecting text but got something else: "%2$s"', 'wp-analytify' ), esc_html( $context ), esc_html( $data ) ) ); |
| 94 |
} |
| 95 |
$data = $text; |
| 96 |
} elseif ( 'serialized' === $key_rules ) { |
| 97 |
if ( ! is_string( $data ) || ! is_serialized( $data ) ) { |
| 98 |
// translators: Serialized data error. |
| 99 |
wp_die( sprintf( esc_html__( '%1$s was expecting serialized data but got something else: "%2$s"', 'wp-analytify' ), esc_html( $context ), is_array( $data ) ? 'array' : esc_html( $data ) ) ); |
| 100 |
} |
| 101 |
} elseif ( 'numeric' === $key_rules ) { |
| 102 |
if ( ! is_numeric( $data ) ) { |
| 103 |
// translators: Valid numeric error. |
| 104 |
wp_die( sprintf( esc_html__( '%1$s was expecting a valid numeric but got something else: "%2$s"', 'wp-analytify' ), esc_html( $context ), is_array( $data ) ? 'array' : esc_html( $data ) ) ); |
| 105 |
} |
| 106 |
} elseif ( 'int' === $key_rules ) { |
| 107 |
// As we are sanitizing form data, even integers are within a string. |
| 108 |
if ( ! is_numeric( $data ) || (int) $data !== $data ) { |
| 109 |
// translators: Integer Error. |
| 110 |
wp_die( sprintf( esc_html__( '%1$s was expecting an integer but got something else: "%2$s"', 'wp-analytify' ), esc_html( $context ), is_array( $data ) ? 'array' : esc_html( $data ) ) ); |
| 111 |
} |
| 112 |
$data = (int) $data; |
| 113 |
} elseif ( 'positive_int' === $key_rules ) { |
| 114 |
if ( ! is_numeric( $data ) ) { |
| 115 |
// translators: Positive Integer Error. |
| 116 |
wp_die( sprintf( esc_html__( '%1$s was expecting a positive number (int) but got something else: "%2$s"', 'wp-analytify' ), esc_html( $context ), is_array( $data ) ? 'array' : esc_html( $data ) ) ); |
| 117 |
} |
| 118 |
$data = (int) $data; |
| 119 |
if ( $data <= 0 ) { |
| 120 |
// translators: Positive Integer Error. |
| 121 |
wp_die( sprintf( esc_html__( '%1$s was expecting a positive number (int) but got something else: "%2$s"', 'wp-analytify' ), esc_html( $context ), is_array( $data ) ? 'array' : esc_html( $data ) ) ); |
| 122 |
} |
| 123 |
} elseif ( 'negative_int' === $key_rules ) { |
| 124 |
if ( ! is_numeric( $data ) ) { |
| 125 |
// translators: Negative Integer Error. |
| 126 |
wp_die( sprintf( esc_html__( '%1$s was expecting a negative number (int) but got something else: "%2$s"', 'wp-analytify' ), esc_html( $context ), is_array( $data ) ? 'array' : esc_html( $data ) ) ); |
| 127 |
} |
| 128 |
$data = (int) $data; |
| 129 |
if ( $data >= 0 ) { |
| 130 |
// translators: Negative Integer Error. |
| 131 |
wp_die( sprintf( esc_html__( '%1$s was expecting a negative number (int) but got something else: "%2$s"', 'wp-analytify' ), esc_html( $context ), is_array( $data ) ? 'array' : esc_html( $data ) ) ); |
| 132 |
} |
| 133 |
} elseif ( 'zero_int' === $key_rules ) { |
| 134 |
if ( ! is_numeric( $data ) ) { |
| 135 |
// translators: Zero Integer Error. |
| 136 |
wp_die( sprintf( esc_html__( '%1$s was expecting 0 (int) but got something else: "%2$s"', 'wp-analytify' ), esc_html( $context ), is_array( $data ) ? 'array' : esc_html( $data ) ) ); |
| 137 |
} |
| 138 |
$data = (int) $data; |
| 139 |
if ( 0 !== $data ) { |
| 140 |
// translators: Zero Integer Error. |
| 141 |
wp_die( sprintf( esc_html__( '%1$s was expecting 0 (int) but got something else: "%2$s"', 'wp-analytify' ), esc_html( $context ), is_array( $data ) ? 'array' : esc_html( $data ) ) ); |
| 142 |
} |
| 143 |
} elseif ( 'empty' === $key_rules ) { |
| 144 |
if ( ! empty( $data ) ) { |
| 145 |
// translators: Empty Value Error. |
| 146 |
wp_die( sprintf( esc_html__( '%1$s was expecting an empty value but got something else: "%2$s"', 'wp-analytify' ), esc_html( $context ), is_array( $data ) ? 'array' : esc_html( $data ) ) ); |
| 147 |
} |
| 148 |
} elseif ( 'url' === $key_rules ) { |
| 149 |
if ( ! is_string( $data ) ) { |
| 150 |
// translators: URL Error. |
| 151 |
wp_die( sprintf( esc_html__( '%1$s was expecting a string for URL sanitization but got something else: "%2$s"', 'wp-analytify' ), esc_html( $context ), is_array( $data ) ? 'array' : esc_html( $data ) ) ); |
| 152 |
} |
| 153 |
$url = esc_url_raw( $data ); |
| 154 |
if ( empty( $url ) ) { |
| 155 |
// translators: URL Error. |
| 156 |
wp_die( sprintf( esc_html__( '%1$s was expecting a URL but got something else: "%2$s"', 'wp-analytify' ), esc_html( $context ), esc_html( $data ) ) ); |
| 157 |
} |
| 158 |
$data = $url; |
| 159 |
} elseif ( 'bool' === $key_rules ) { |
| 160 |
if ( ! is_string( $data ) ) { |
| 161 |
// translators: Bool Error. |
| 162 |
wp_die( sprintf( esc_html__( '%1$s was expecting a string for bool sanitization but got something else: "%2$s"', 'wp-analytify' ), esc_html( $context ), is_array( $data ) ? 'array' : esc_html( $data ) ) ); |
| 163 |
} |
| 164 |
$bool = sanitize_key( $data ); |
| 165 |
if ( empty( $bool ) || ! in_array( $bool, array( 'true', 'false' ), true ) ) { |
| 166 |
// translators: Bool Error. |
| 167 |
wp_die( sprintf( esc_html__( '%1$s was expecting a bool but got something else: "%2$s"', 'wp-analytify' ), esc_html( $context ), esc_html( $data ) ) ); |
| 168 |
} |
| 169 |
$data = $bool; |
| 170 |
} else { |
| 171 |
// translators: Unknown Error. |
| 172 |
wp_die( sprintf( esc_html__( 'Unknown sanitization rule "%1$s" supplied by %2$s', 'wp-analytify' ), esc_html( $key_rules ), esc_html( $context ) ) ); |
| 173 |
} |
| 174 |
|
| 175 |
return $data; |
| 176 |
} |
| 177 |
} |
| 178 |
|