<?php
/**
* @version 1.0
* @package Booking Calendar
* @subpackage Security: Escaping & Sanitizing Functions
* @category Functions
*
* @author wpdevelop
* @link https://wpbookingcalendar.com/
* @email [email protected]
*
* @modified 2024-09-03
*/
if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
// =====================================================================================================================
// == Security: Escaping & Sanitizing ==
// =====================================================================================================================
/**
* Check specific parameters in ARRAY and return cleaned params or default values
*
* @param array $request_params_values_arr = / think like in $_REQUEST parameter /
* array(
* 'page_num' => 1,
* 'page_items_count' => 10,
* 'sort' => 'rule_id',
* 'sort_type' => 'DESC',
* 'status' => '',
* 'keyword' => '',
* 'create_date' => ''
* )
* @param array $params_rules = array(
* 'page_num' => array( 'validate' => 'd', 'default' => 1 )
* , 'page_items_count' => array( 'validate' => 'd', 'default' => 10 )
* , 'sort' => array( 'validate' => array( 'rule_id' ), 'default' => 'rule_id' )
* , 'sort_type' => array( 'validate' => array( 'ASC', 'DESC'),'default' => 'DESC' )
* , 'status' => array( 'validate' => 's', 'default' => '' )
* , 'keyword' => array( 'validate' => 's', 'default' => '' )
* , 'create_date' => array( 'validate' => 'date', 'default' => '' )
* )
*
*
* 'd'; // '1' | ''
* 's'; // string !!! Clean 'LIKE' string for DB !!!
* 'digit_or_csd'; // '0' | '1,2,3' | ''
* 'digit_or_date'; // number | date 2016-07-20
*
* 'checked_skip_it' // Skip checking
* array( '0', 'trash', 'any'); // Elements only listed in array
*
*@return array $clean_params = Array (
* [page_num] => 3
* [page_items_count] => 20
* [sort] => booking_id
* [sort_type] => DESC
* [keyword] =>
* [source] =>
* [create_date] =>
* )
*
*
Example of Direct Clean Params:
$request_params_ajx_booking = array(
'page_num' => array( 'validate' => 'd', 'default' => 1 )
, 'page_items_count' => array( 'validate' => 'd', 'default' => 10 )
, 'sort' => array( 'validate' => array( 'ajx_booking_id' ), 'default' => 'ajx_booking_id' )
, 'sort_type' => array( 'validate' => array( 'ASC', 'DESC'),'default' => 'DESC' )
, 'status' => array( 'validate' => 's', 'default' => '' )
, 'keyword' => array( 'validate' => 's', 'default' => '' )
, 'ru_create_date' => array( 'validate' => 'date', 'default' => '' )
);
$request_params_values = array( // Usually $request_params_values is $_REQUEST
'page_num' => 1,
'page_items_count' => 3,
'sort' => 'ajx_booking_id',
'sort_type' => 'DESC',
'status' => '',
'keyword' => '',
'ru_create_date' => ''
);
$request_params = wpbc_sanitize_params_in_arr( $request_params_values, $request_params_ajx_booking );
*/
function wpbc_sanitize_params_in_arr( $request_params_values_arr, $params_rules ){
$clean_params = array();
foreach ( $params_rules as $request_key_name => $clean_type ) {
if ( isset( $request_params_values_arr[ $request_key_name ] ) ) {
$request_value_check = $request_params_values_arr[ $request_key_name ];
} else {
$request_value_check = false;
}
// If not defined in VALUES (think like in $_REQUEST parameter), then get default value
if ( false === $request_value_check ) {
// D E F A U L T
$clean_params[ $request_key_name ] = $params_rules[ $request_key_name ]['default'];
} else {
// C L E A N I N G
$clean_type = $params_rules[ $request_key_name ]['validate'];
// Check only values from this Array
if ( is_array( $clean_type ) ) {
$clean_type = array_map( 'strtolower', $clean_type );
if ( ( isset( $request_value_check ) ) && ( ! in_array( strtolower( $request_value_check ), $clean_type ) ) ) {
$clean_type = 'checked_skip_it';
$request_value_check = $params_rules[ $request_key_name ]['default']; // Reset it, if value not in array And get default value
} else {
$clean_type = 'checked_skip_it';
}
}
switch ( $clean_type ) {
case 'checked_skip_it':
$clean_params[ $request_key_name ] = $request_value_check;
break;
case 'date': // Date
$clean_params[ $request_key_name ] = wpbc_sanitize_date( $request_value_check );
break;
case 'csv_dates': // CSV Dates: '11.11.2025, 12.11.2025, 13.11.2025' or '2024-02-06, 2024-02-10'
$clean_params[ $request_key_name ] = wpbc_sanitize_csv_dates( $request_value_check ); // FixIn: 9.9.1.1.
break;
case 'digit_or_date': // digit or Date
$clean_params[ $request_key_name ] = wpbc_sanitize_digit_or_date( $request_value_check );
break;
case 'digit_or_csd': // digit or comma separated digit
$clean_params[ $request_key_name ] = wpbc_sanitize_digit_or_csd( $request_value_check );
break;
case 's': // string
$clean_params[ $request_key_name ] = wpbc_sanitize_text( $request_value_check );
break;
case 'strong': // string
$clean_params[ $request_key_name ] = wpbc_sanitize_text_strong( $request_value_check );
break;
case 'array':
if ( is_array( $request_value_check ) ) {
foreach ( $request_value_check as $check_arr_index => $check_arr_value ) {
$request_value_check[ $check_arr_index ] = wpbc_sanitize_text( $check_arr_value ); // Check each option as string
}
$clean_params[ $request_key_name ] = $request_value_check;
} else {
$clean_params[ $request_key_name ] = $params_rules[ $request_key_name ]['default'];
}
break;
case 'digit_or_empty': // digit or ''
if ( '' === $request_value_check) {
$clean_params[ $request_key_name ] = '';
} else {
$clean_params[ $request_key_name ] = intval( $request_value_check );
}
break;
case 'float_or_empty': // digit or ''
if ( '' === $request_value_check) {
$clean_params[ $request_key_name ] = '';
} else {
// In case if was entered 10,99 instead of 10.99
$request_value_check = str_replace( ',', '.', $request_value_check );
$clean_params[ $request_key_name ] = floatval( $request_value_check );
}
break;
case 'f': // float
$clean_params[ $request_key_name ] = floatval( $request_value_check );
break;
case 'd': // digit
$clean_params[ $request_key_name ] = intval( $request_value_check );
break;
default:
$clean_params[ $request_key_name ] = intval( $request_value_check );
break;
}
}
}
return $clean_params;
}
/**
* Check parameter if it number or comma separated list of numbers
*
* @param string | array $value
*
* @return string | array
*
* Example:
* wpbc_sanitize_digit_or_csd( '12,a,45,9' ) => '12,0,45,9'
* or
* wpbc_sanitize_digit_or_csd( '10a' ) => '10
* or
* wpbc_sanitize_digit_or_csd( array( '12,a,45,9', '10a' ) ) => array ( '12,0,45,9', '10' )
*/
function wpbc_sanitize_digit_or_csd( $value ) { // FixIn: 6.2.1.4.
if ( $value === '' ) {
return $value;
}
if ( is_array( $value ) ) {
foreach ( $value as $key => $check_value ) {
$value[ $key ] = wpbc_sanitize_digit_or_csd( $check_value );
}
return $value;
}
$value = str_replace( ';', ',', $value );
$array_of_nums = explode( ',', $value );
$result = array();
foreach ( $array_of_nums as $check_element ) {
$result[] = intval( $check_element );
}
$result = implode( ',', $result );
return $result;
}
/**
* Check about Valid date or number, like 2016-07-20 and return this date or number
*
* @param string $value
*
* @return string | int '2022-05-31' or 5 or ''
*/
function wpbc_sanitize_digit_or_date( $value ) { // FixIn: 6.2.1.4.
if ( $value === '' ) return $value;
if ( preg_match("/^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])$/", $value ) ) {
return $value; // Date is valid in format: 2016-07-20
} else {
return intval( $value );
}
}
/**
* Check about Valid date, like 2016-07-20 and return this date or ''
*
* @param string $value
*
* @return string '2022-05-31' or ''
*/
function wpbc_sanitize_date( $value ) { // FixIn: 6.2.1.4.
if ( $value === '' ) return $value;
if ( preg_match("/^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])$/", $value ) ) {
return $value; // Date is valid in format: 2016-07-20
} else {
return '';
}
}
/**
* Check about Valid date, like '31.05.2022 and return this date or ''
*
* @param string $value
*
* @return string '31.05.2022' or ''
*/
function wpbc_sanitize_date_dmy( $value ) { // FixIn: 9.9.1.1.
if ( $value === '' ) return $value;
if ( preg_match("/^(0[1-9]|[1-2][0-9]|3[0-1]).(0[1-9]|1[0-2]).[0-9]{4}$/", $value ) ) {
return $value; // Date is valid in format: 31.05.2022
} else {
return '';
}
}
/**
* Check about Valid date(s), like CSV Dates: such as: '11.11.2025, 12.11.2025, 13.11.2025' or '2024-02-06, 2024-02-10' and then return them sanitized dates or ''
*
* @param string $value
*
* @return string '2022-05-31' or ''
*/
function wpbc_sanitize_csv_dates( $value ) { // FixIn: 9.9.1.1.
if ( '' === $value ) { return $value; }
$value = str_replace( ';', ',', $value );
$array_of_nums = explode( ',', $value );
$result = array();
foreach ( $array_of_nums as $single_date ) {
$single_date = trim( $single_date );
// Check for date '2024-02-06
$date_ymd = wpbc_sanitize_date( $single_date );
if ( '' !== $date_ymd ) {
$result[] = $date_ymd;
} else {
// Otherwise check for date: '06.02.2024'
$date_dmy = wpbc_sanitize_date_dmy( $single_date );
if ( '' !== $date_dmy ) {
$result[] = $date_dmy;
}
}
}
$result = implode( ',', $result );
return $result;
}
/**
* Escape string from SQL for the HTML form field
*
* @param string $value
*
* @return string
*
* Used: esc_sql function.
*
* https://codex.wordpress.org/Function_Reference/esc_sql
* Note: Be careful to use this function correctly. It will only escape values to be used in strings in the query.
* That is, it only provides escaping for values that will be within quotes in the SQL (as in field = '{$escaped_value}').
* If your value is not going to be within quotes, your code will still be vulnerable to SQL injection.
* For example, this is vulnerable, because the escaped value is not surrounded by quotes in the SQL query:
* ORDER BY {$escaped_value}. As such, this function does not escape unquoted numeric values, field names, or SQL keywords.
*
*/
function wpbc_sanitize_text( $value ){
$value_trimmed = trim( stripslashes( $value ) ); // \' becomes ' and so on
$esc_sql_value = sanitize_textarea_field( $value_trimmed ); // preserves new lines (\n) and other whitespace
//$esc_sql_value = sanitize_text_field( $value_trimmed ); // remove new lines (\n) and other whitespace
//global $wpdb;
//$value = trim( $wpdb->prepare( "'%s'", $esc_sql_value ) , "'" );
//$esc_sql_value = trim( stripslashes( $esc_sql_value ) );
return $esc_sql_value;
}
/**
* Escape string from SQL for the HTML form field
*
* @param string $value
*
* @return string
*
* Used: esc_sql function.
*
* https://codex.wordpress.org/Function_Reference/esc_sql
* Note: Be careful to use this function correctly. It will only escape values to be used in strings in the query.
* That is, it only provides escaping for values that will be within quotes in the SQL (as in field = '{$escaped_value}').
* If your value is not going to be within quotes, your code will still be vulnerable to SQL injection.
* For example, this is vulnerable, because the escaped value is not surrounded by quotes in the SQL query:
* ORDER BY {$escaped_value}. As such, this function does not escape unquoted numeric values, field names, or SQL keywords.
*
*/
function wpbc_sanitize_text_strong( $value ){
$value_trimmed = trim( stripslashes( $value ) ); // \' becomes ' and so on
//$esc_sql_value = sanitize_text_field( $value_trimmed ); // remove new lines (\n) and other whitespace
$esc_sql_value = sanitize_textarea_field( $value_trimmed ); // preserves new lines (\n) and other whitespace
// clean any tags
$esc_sql_value = preg_replace( '/<[^>]*>/', '', $esc_sql_value );
$esc_sql_value = str_replace( '<', ' ', $esc_sql_value );
$esc_sql_value = str_replace( '>', ' ', $esc_sql_value );
$esc_sql_value = wp_strip_all_tags( $esc_sql_value );
//FixIn: 9.7.4.1 - escape coded html/xss // Escape any XSS injection
// If we have field converted to 'Unicode Hex Character Code', then we make HTML decode firstly (html_entity_decode) and then make sanitizing
$esc_sql_value = sanitize_textarea_field( html_entity_decode( $esc_sql_value ) );
// $esc_sql_value = str_replace('%', '%', $esc_sql_value ); // clean any % from the form, because otherwise, there is problems with SQL prepare function
// $esc_sql_value = str_replace('_', '_', $esc_sql_value ); // clean any _
// $esc_sql_value = str_replace('^', '^', $esc_sql_value ); // clean any ^ caret symbols
// $esc_sql_value = str_replace('~', '~', $esc_sql_value ); // clean any ~ equivalency sign - tilde
return $esc_sql_value;
}
// ---------------------------------------------------------------------------------------------------------------------
// Other Sanitize functions
// ---------------------------------------------------------------------------------------------------------------------
/**
* Sanitize $_GET, $_POST, $_REQUEST text parameters // FixIn: 10.0.0.12.
*
* @param $value
* @param $keep_newlines bool
*
* @return string
*/
function wpbc_clean_text_value( $value , $keep_newlines = false ){
if ( $keep_newlines ) {
$value_cleaned = sanitize_textarea_field( $value );
} else {
$value_cleaned = sanitize_text_field( $value );
}
return $value_cleaned;
}
// check $value for injection here
function wpbc_clean_parameter( $value, $is_escape_sql = true ) {
$value = preg_replace( '/<[^>]*>/', '', $value ); // clean any tags
$value = str_replace( '<', ' ', $value );
$value = str_replace( '>', ' ', $value );
$value = wp_strip_all_tags( $value );
//FixIn: 9.7.4.1 - escape coded html/xss // Escape any XSS injection
$value = sanitize_textarea_field( $value );
$value = sanitize_textarea_field( html_entity_decode( $value ) ); // If we have field converted to 'Unicode Hex Character Code', then we make HTML decode firstly (html_entity_decode) and then make sanitizing
if ( $is_escape_sql ) {
$value = esc_sql( $value ); // Clean SQL injection // FixIn: 9.7.4.2.
}
$value = esc_textarea( $value ); // FixIn: 7.1.1.2.
return $value;
}
/**
* Check parameter if it number or comma separated list of numbers
*
* @param $value
* @return array|string
*
* Example:
* wpbc_clean_digit_or_csd( '12,a,45,9' ) => '12,0,45,9'
* or
* wpbc_clean_digit_or_csd( '10a' ) => '10
* or
* wpbc_clean_digit_or_csd( array( '12,a,45,9', '10a' ) ) => array ( '12,0,45,9', '10' )
*/
function wpbc_clean_digit_or_csd( $value ) { // FixIn: 6.2.1.4.
if ( $value === '' ) return $value;
if ( is_array( $value ) ) {
foreach ( $value as $key => $check_value ) {
$value[ $key ] = wpbc_clean_digit_or_csd( $check_value );
}
return $value;
}
$value = str_replace( ';', ',', $value );
$array_of_nums = explode(',', $value);
$result = array();
foreach ($array_of_nums as $check_element) {
$result[] = intval( $check_element ); // FixIn: 8.0.2.10.
}
$result = implode(',', $result );
return $result;
}
/**
* Cehck about Valid date, like 2016-07-20 or digit
*
* @param string $value
* @return string or int
*/
function wpbc_clean_digit_or_date( $value ) { // FixIn: 6.2.1.4.
if ( $value === '' ) return $value;
if ( preg_match("/^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])$/", $value ) ) {
return $value; // Date is valid in format: 2016-07-20
} else {
return intval( $value );
}
}
/**
* Check about Valid dat in format '2024-05-08' otherwise return ''
*
* @param string $value
*
* @return string date or '' if date was not valie
*/
function wpbc_clean_date( $value ) {
if (
( ! empty( $value ) )
&& ( preg_match( "/^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])$/", $value ) )
){
return $value; // Date is valid in format: 2024-05-08
}
return ''; // Date not Valid
}
/**
* Escape any XSS injection from values in booking form
*
* @param array $structured_booking_data_arr [...]
*
* @return array [...]
*/
function wpbc_escape_any_xss_in_arr( $structured_booking_data_arr ) {
foreach ( $structured_booking_data_arr as $field_name => $field_value ) {
if ( is_array( $field_value ) ) {
$structured_booking_data_arr[ $field_name ] = wpbc_escape_any_xss_in_arr( $field_value );
} else {
$is_escape_sql = false; // Do not replace %
$field_value_cleaned = wpbc_escape_any_xss_in_string( $field_value, $is_escape_sql );
$structured_booking_data_arr[ $field_name ] = $field_value_cleaned;
}
}
return $structured_booking_data_arr;
}
/**
* Escape any XSS injection from string values
*
* @param string $field_value
*
* @return string
*/
function wpbc_escape_any_xss_in_string( $field_value, $is_escape_sql = true ) {
$field_value_cleaned = wpbc_clean_parameter( $field_value, $is_escape_sql );
$field_value_cleaned = str_replace( '%', '%', $field_value_cleaned ); // clean % in form, because can be problems with SQL prepare function
return $field_value_cleaned;
}
function wpbc_esc_like( $value_trimmed ) {
global $wpdb;
if ( method_exists( $wpdb ,'esc_like' ) )
return $wpdb->esc_like( $value_trimmed ); // Its require minimum WP 4.0.0
else
return addcslashes( $value_trimmed, '_%\\' ); // Direct implementation from $wpdb->esc_like(
}
/**
* Escape single quote from ' to '
*
* @param string $value - String to escape.
*
* @return array|string|string[]
*/
function wpbc_esc_single_quote( $value ) {
$safe_text = wp_check_invalid_utf8( $value );
$escaped_value = str_replace( "'", ''', $safe_text );
return $escaped_value;
}
/**
* Sanitize term to Slug format (no spaces, lowercase).
* urldecode - reverse munging of UTF8 characters.
*
* @param mixed $value
* @return string
*/
function wpbc_get_slug_format( $value ) {
return urldecode( sanitize_title( $value ) );
}
/**
* Clean user string for using in SQL LIKE statement - append to LIKE sql
*
* @param string $value - to clean
* @return string - escaped
* Exmaple:
* $search_escaped_like_title = wpbc_clean_like_string_for_append_in_sql_for_db( $input_var );
*
* $where_sql = " WHERE title LIKE ". $search_escaped_like_title ." ";
*/
function wpbc_clean_like_string_for_append_in_sql_for_db( $value ) {
global $wpdb;
$value_trimmed = trim( stripslashes( $value ) );
$wild = '%';
$like = $wild . wpbc_esc_like( $value_trimmed ) . $wild;
/* phpcs:ignore WordPress.DB.PreparedSQLPlaceholders.QuotedSimplePlaceholder */
$sql = $wpdb->prepare( "'%s'", $like );
return $sql;
/* Help:
* First half of escaping for LIKE special characters % and _ before preparing for MySQL.
* Use this only before wpdb::prepare() or esc_sql(). Reversing the order is very bad for security.
*
* Example Prepared Statement:
*
* $wild = '%';
* $find = 'only 43% of planets';
* $like = $wild . wpbc_esc_like( $find ) . $wild;
* $sql = $wpdb->prepare( "SELECT * FROM $wpdb->posts WHERE post_content LIKE '%s'", $like );
*
* Example Escape Chain:
*
* $sql = esc_sql( wpbc_esc_like( $input ) );
*/
}
/**
* Clean string for using in SQL LIKE requests inside single quotes: WHERE title LIKE '%". $escaped_search_title ."%'
* Replaced _ to \_ % to \% \ to \\
* @param string $value - to clean
* @return string - escaped
* Exmaple:
* $search_escaped_like_title = wpbc_clean_like_string_for_db( $input_var );
*
* $where_sql = " WHERE title LIKE '%". $search_escaped_like_title ."%' ";
*
* Important! Use SINGLE quotes after in SQL query: LIKE '%".$data."%'
*/
function wpbc_clean_like_string_for_db( $value ){
global $wpdb;
$value_trimmed = trim( stripslashes( $value ) );
$value_trimmed = wpbc_esc_like( $value_trimmed );
/* phpcs:ignore WordPress.DB.PreparedSQLPlaceholders.QuotedSimplePlaceholder */
$value = trim( $wpdb->prepare( "'%s'", $value_trimmed ), "'" );
return $value;
/* Help:
* First half of escaping for LIKE special characters % and _ before preparing for MySQL.
* Use this only before wpdb::prepare() or esc_sql(). Reversing the order is very bad for security.
*
* Example Prepared Statement:
*
* $wild = '%';
* $find = 'only 43% of planets';
* $like = $wild . wpbc_esc_like( $find ) . $wild;
* $sql = $wpdb->prepare( "SELECT * FROM $wpdb->posts WHERE post_content LIKE '%s'", $like );
*
* Example Escape Chain:
*
* $sql = esc_sql( wpbc_esc_like( $input ) );
*/
}
/**
* Escape string from SQL for the HTML form field
*
* @param string $value
* @return string
*
* Used: esc_sql function.
*
* https://codex.wordpress.org/Function_Reference/esc_sql
* Note: Be careful to use this function correctly. It will only escape values to be used in strings in the query.
* That is, it only provides escaping for values that will be within quotes in the SQL (as in field = '{$escaped_value}').
* If your value is not going to be within quotes, your code will still be vulnerable to SQL injection.
* For example, this is vulnerable, because the escaped value is not surrounded by quotes in the SQL query:
* ORDER BY {$escaped_value}. As such, this function does not escape unquoted numeric values, field names, or SQL keywords.
*
*/
function wpbc_clean_string_for_form( $value ){
global $wpdb;
$value_trimmed = trim( stripslashes( $value ) );
//FixIn: 8.0.2.10 //Fix for update of WP 4.8.3
if ( method_exists( $wpdb, 'remove_placeholder_escape' ) )
$esc_sql_value = $wpdb->remove_placeholder_escape( esc_sql( $value_trimmed ) );
else
$esc_sql_value = esc_sql( $value_trimmed );
//$value = trim( $wpdb->prepare( "'%s'", $esc_sql_value ) , "'" );
$esc_sql_value = trim( stripslashes( $esc_sql_value ) );
return $esc_sql_value;
}
/**
* Escape shortcode parameters
*
* @param array $attr
*
* @return array
*/
function wpbc_escape_shortcode_params( $attr ) {
if ( is_array( $attr ) ) {
$scaped_attr = array();
foreach ( $attr as $attr_key => $attr_val ) {
$attr_key = sanitize_text_field( $attr_key ); // FixIn: 10.11.2.1.
$attr_val = sanitize_text_field( $attr_val ); // FixIn: 10.11.2.1.
$scaped_attr[ $attr_key ] = $attr_val;
}
return $scaped_attr;
}
if ( is_string( $attr ) ) {
$scaped_attr = sanitize_text_field( $attr ); // FixIn: 10.11.2.1.
return $scaped_attr;
}
return $attr;
}