HEX
Server: Apache
System: Linux outside 5.4.8_Algonet_ZeroMAC_0.9.4.8 #6 SMP Mon Feb 3 20:36:07 CET 2020 x86_64
User: server (1002)
PHP: 8.3.20
Disabled: exec,passthru,shell_exec,system,proc_open,popen,curl_multi_exec,parse_ini_file,show_source
Upload Files
File: /home/muoapartman/www/wp-content/plugins/motopress-hotel-booking-lite/functions.php
<?php

use MPHB\Utils\DateUtils;

/**
 * Get template part.
 *
 * @param string $slug
 * @param string $name Optional. Default ''.
 */
function mphb_get_template_part( $slug, $atts = array() ){

	$template = '';

	// Look in %theme_dir%/%template_path%/slug.php
	$template = locate_template( MPHB()->getTemplatePath() . "{$slug}.php" );

	// Get default template from plugin
	if ( empty( $template ) && file_exists( MPHB()->getPluginPath( "templates/{$slug}.php" ) ) ) {
		$template = MPHB()->getPluginPath( "templates/{$slug}.php" );
	}

	// Allow 3rd party plugins to filter template file from their plugin.
	$template = apply_filters( 'mphb_get_template_part', $template, $slug, $atts );

	if ( !empty( $template ) ) {
		mphb_load_template( $template, $atts );
	}
}

function mphb_load_template( $template, $templateArgs = array() ){
	if ( $templateArgs && is_array( $templateArgs ) ) {
		extract( $templateArgs );
	}
	require $template;
}

/**
 *
 * @global string $wp_version
 * @param string $type
 * @param bool $gmt
 * @return string
 */
function mphb_current_time( $type, $gmt = 0 ){
	global $wp_version;
	if ( version_compare( $wp_version, '3.9', '<=' ) && !in_array( $type, array( 'timestmap',
			'mysql' ) ) ) {
		$timestamp = current_time( 'timestamp', $gmt );
		return date( $type, $timestamp );
	} else {
		return current_time( $type, $gmt );
	}
}

/**
 * Retrieve a post status label by name
 *
 * @param string $status
 * @return string
 */
function mphb_get_status_label( $status ){
	switch ( $status ) {
		case 'new':
			$label		 = _x( 'New', 'Post Status', 'motopress-hotel-booking' );
			break;
		case 'auto-draft':
			$label		 = _x( 'Auto Draft', 'Post Status', 'motopress-hotel-booking' );
			break;
		default:
			$statusObj	 = get_post_status_object( $status );
			$label		 = !is_null( $statusObj ) && property_exists( $statusObj, 'label' ) ? $statusObj->label : '';
			break;
	}

	return $label;
}

/**
 *
 * @param string $name
 * @param string $value
 * @param int $expire
 */
function mphb_set_cookie( $name, $value, $expire = 0 ){
	setcookie( $name, $value, $expire, COOKIEPATH, COOKIE_DOMAIN );
	if ( COOKIEPATH != SITECOOKIEPATH ) {
		setcookie( $name, $value, $expire, SITECOOKIEPATH, COOKIE_DOMAIN );
	}
}

/**
 *
 * @param string $name
 * @return mixed|null Cookie value or null if not exists.
 */
function mphb_get_cookie( $name ){
	return ( isset( $_COOKIE[$name] ) ) ? sanitize_text_field( wp_unslash( $_COOKIE[$name] ) ) : null;
}

/**
 *
 * @param string $name
 * @return bool
 */
function mphb_has_cookie( $name ){
	return isset( $_COOKIE[$name] );
}

/**
 * 
 * @param string $name
 * 
 * @since 4.2.0
 */
function mphb_unset_cookie( $name ) {
	if( isset( $_COOKIE[$name] ) ) {
		unset( $_COOKIE[$name] );
	}
}

function mphb_is_checkout_page(){
	$checkoutPageId = MPHB()->settings()->pages()->getCheckoutPageId();
	return $checkoutPageId && is_page( $checkoutPageId );
}

function mphb_is_search_results_page(){
	$searchResultsPageId = MPHB()->settings()->pages()->getSearchResultsPageId();
	return $searchResultsPageId && is_page( $searchResultsPageId );
}

function mphb_is_single_room_type_page(){
	return is_singular( MPHB()->postTypes()->roomType()->getPostType() );
}

function mphb_is_create_booking_page(){
	return false;
}

function mphb_get_thumbnail_width(){
	$width = 150;

	$imageSizes = get_intermediate_image_sizes();
	if ( in_array( 'thumbnail', $imageSizes ) ) {
		$width = (int) get_option( "thumbnail_size_w", $width );
	}

	return $width;
}

/**
 *
 * @param float $price
 * @param array $atts
 * @param string $atts['decimal_separator']
 * @param string $atts['thousand_separator']
 * @param int $atts['decimals'] Number of decimals
 * @param string $atts['currency_position'] Possible values: after, before, after_space, before_space
 * @param string $atts['currency_symbol']
 * @param bool $atts['literal_free'] Use "Free" text instead of 0 price.
 * @param bool $atts['trim_zeros'] Trim decimals zeros.
 * @return string
 */
function mphb_format_price( $price, $atts = array() ){

	$defaultAtts = array(
		'decimal_separator'	 => MPHB()->settings()->currency()->getPriceDecimalsSeparator(),
		'thousand_separator' => MPHB()->settings()->currency()->getPriceThousandSeparator(),
		'decimals'			 => MPHB()->settings()->currency()->getPriceDecimalsCount(),
		'currency_position'	 => MPHB()->settings()->currency()->getCurrencyPosition(),
		'currency_symbol'	 => MPHB()->settings()->currency()->getCurrencySymbol(),
		'literal_free'		 => false,
		'trim_zeros'		 => true,
		'period'			 => false,
		'period_title'		 => '',
		'period_nights'		 => 1,
		'as_html'			 => true
	);

	$atts = wp_parse_args( $atts, $defaultAtts );

	$price_and_atts = apply_filters( 'mphb_format_price_parameters', array( 'price' => $price, 'attributes' => $atts ) );
	$price = $price_and_atts[ 'price' ];
	$atts = $price_and_atts[ 'attributes' ];

	$priceFormat = MPHB()->settings()->currency()->getPriceFormat( $atts['currency_symbol'], $atts['currency_position'], $atts['as_html'] );

	$priceClasses = array( 'mphb-price' );

	if ( $atts['literal_free'] && $price == 0 ) {
		$formattedPrice	 = apply_filters( 'mphb_free_literal', _x( 'Free', 'Zero price', 'motopress-hotel-booking' ) );
		$priceClasses[]	 = 'mphb-price-free';
	} else {
		$negative	 = $price < 0;
		$price		 = abs( $price );
		$price		 = number_format( $price, $atts['decimals'], $atts['decimal_separator'], $atts['thousand_separator'] );
		if ( $atts['trim_zeros'] ) {
			$price = mphb_trim_zeros( $price );
		}
		$formattedPrice = ( $negative ? '-' : '' ) . sprintf( $priceFormat, $price );
	}

	/**
	 * @since 3.9.8
	 *
	 * @param array $priceClasses
	 * @param float $price 
	 * @param string $formattedPrice
	 * @param array $atts
	 */
	$priceClasses = apply_filters( 'mphb_price_classes', $priceClasses, $price, $formattedPrice, $atts );

	if ( $atts['as_html'] ) {
		$priceClassesStr = join( ' ', $priceClasses );
		$price = sprintf( '<span class="%s">%s</span>', esc_attr( $priceClassesStr ), $formattedPrice );
	} else {
		$price = $formattedPrice;
	}

	if ( $atts['period'] ) {

		$priceDescription	 = _nx( 'per night', 'for %d nights', $atts['period_nights'], 'Ex: $99 for 2 nights', 'motopress-hotel-booking' );
		$priceDescription	 = sprintf( $priceDescription, $atts['period_nights'] );
		$priceDescription	 = apply_filters( 'mphb_price_period_description', $priceDescription, $atts['period_nights'] );

		if ( $atts['as_html'] ) {
			$priceDescription = sprintf( '<span class="mphb-price-period" title="%1$s">%2$s</span>', esc_attr( $atts['period_title'] ), $priceDescription );
		}

		$price = sprintf( '%1$s %2$s', $price, $priceDescription );
	}

	return $price;
}

/**
 *
 * @param float $price
 * @param array $atts
 * @param string $atts['decimal_separator']
 * @param string $atts['thousand_separator']
 * @param int $atts['decimals'] Number of decimals
 * @return string
 */
function mphb_format_percentage( $price, $atts = array() ){

	$defaultAtts = array(
		'decimal_separator'	 => MPHB()->settings()->currency()->getPriceDecimalsSeparator(),
		'thousand_separator' => MPHB()->settings()->currency()->getPriceThousandSeparator(),
		'decimals'			 => MPHB()->settings()->currency()->getPriceDecimalsCount()
	);

	$atts = wp_parse_args( $atts, $defaultAtts );

	$isNegative		 = $price < 0;
	$price			 = abs( $price );
	$price			 = number_format( $price, $atts['decimals'], $atts['decimal_separator'], $atts['thousand_separator'] );
	$formattedPrice	 = ( $isNegative ? '-' : '' ) . $price;

	return '<span class="mphb-percentage">' . $formattedPrice . '%</span>';
}

/**
 * Trim trailing zeros off prices.
 *
 * @param mixed $price
 * @return string
 */
function mphb_trim_zeros( $price ){
	return preg_replace( '/' . preg_quote( MPHB()->settings()->currency()->getPriceDecimalsSeparator(), '/' ) . '0++$/', '', $price );
}

/**
 * @since 3.2.0
 */
function mphb_trim_decimal_zeros($price)
{
    $separator = preg_quote(MPHB()->settings()->currency()->getPriceDecimalsSeparator());

    $price = preg_replace("/{$separator}0++$/", '', $price);
    $price = preg_replace("/({$separator}[^0]++)0++$/", '$1', $price);

    return $price;
}

/**
 * Get WP Query paged var
 *
 * @return int
 */
function mphb_get_paged_query_var(){
	if ( get_query_var( 'paged' ) ) {
		$paged = absint( get_query_var( 'paged' ) );
	} else if ( get_query_var( 'page' ) ) {
		$paged = absint( get_query_var( 'page' ) );
	} else {
		$paged = 1;
	}
	return $paged;
}

/**
 *
 * @param array $queryPart
 * @param array|null $metaQuery
 * @return array
 */
function mphb_add_to_meta_query( $queryPart, $metaQuery ){

	if ( is_null( $metaQuery ) ) {

		if ( mphb_meta_query_is_first_order_clause( $queryPart ) ) {
			$metaQuery = array( $queryPart );
		} else {
			$metaQuery = $queryPart;
		}

		return $metaQuery;
	}

	if ( !empty( $metaQuery ) && !isset( $metaQuery['relation'] ) ) {
		$metaQuery['relation'] = 'AND';
	}

	if ( isset( $metaQuery['relation'] ) && strtoupper( $metaQuery['relation'] ) === 'AND' ) {

		if ( mphb_meta_query_is_first_order_clause( $queryPart ) ||
			( isset( $queryPart['relation'] ) && strtoupper( $queryPart['relation'] ) === 'OR' )
		) {
			$metaQuery[] = $queryPart;
		} else {
			$metaQuery = array_merge( $metaQuery, $queryPart );
		}
	} else {
		$metaQuery = array(
			'relation' => 'AND',
			$queryPart,
			$metaQuery
		);
	}

	return $metaQuery;
}

/**
 *
 * @param array $query
 * @return bool
 */
function mphb_meta_query_is_first_order_clause( $query ){
	return isset( $query['key'] ) || isset( $query['value'] );
}

/**
 * Clean variables using sanitize_text_field. Arrays are cleaned recursively.
 * Non-scalar values are ignored.
 * @param string|array $var
 * @return string|array
 */
function mphb_clean( $var ){
	if ( is_array( $var ) ) {
		return array_map( 'mphb_clean', $var );
	} else {
		return is_scalar( $var ) ? sanitize_text_field( $var ) : $var;
	}
}

/**
 * @see https://github.com/symfony/polyfill-php56
 *
 * @param string $knownString
 * @param string $userInput
 * @return boolean
 */
function mphb_hash_equals( $knownString, $userInput ){

	if ( !is_string( $knownString ) ) {
		return false;
	}

	if ( !is_string( $userInput ) ) {
		return false;
	}

	$knownLen	 = mphb_strlen( $knownString );
	$userLen	 = mphb_strlen( $userInput );

	if ( $knownLen !== $userLen ) {
		return false;
	}

	$result = 0;

	for ( $i = 0; $i < $knownLen; ++$i ) {
		$result |= ord( $knownString[$i] ) ^ ord( $userInput[$i] );
	}

	return 0 === $result;
}

/**
 *
 * @param string $s
 * @return string
 */
function mphb_strlen( $s ){
	return ( extension_loaded( 'mbstring' ) ) ? mb_strlen( $s, '8bit' ) : strlen( $s );
}

/**
 * @todo add support for arrays
 *
 * @param string $url
 * @return array
 */
function mphb_get_query_args( $url ){

	$queryArgs = array();

	$queryStr = parse_url( $url, PHP_URL_QUERY );

	if ( $queryStr ) {
		parse_str( $queryStr, $queryArgs );
	}

	return $queryArgs;
}

/**
 * Wrapper function for wp_dropdown_pages
 *
 * @see wp_dropdown_pages
 *
 * @param array $atts
 * @return string
 */
function mphb_wp_dropdown_pages( $atts = array() ){

	do_action( '_mphb_before_dropdown_pages' );

	// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
	$dropdown = wp_dropdown_pages( $atts );

	do_action( '_mphb_after_dropdown_pages' );

	return $dropdown;
}

/**
 * Wrapper for set_time_limit to see if it is enabled.
 *
 * @param int $limit The maximum execution time, in seconds. If set to zero, no time limit is imposed.
 */
function mphb_set_time_limit( $limit = 0 ){
	if ( function_exists( 'set_time_limit' ) && false === strpos( ini_get( 'disable_functions' ), 'set_time_limit' ) && !ini_get( 'safe_mode' ) ) {
		@set_time_limit( $limit );
	}
}

function mphb_error_log( $message ){
	if ( !is_string( $message ) ) {
		$message = print_r( $message, true );
	}
	error_log( $message );
}

/**
 *
 * @return string
 */
function mphb_current_domain(){
	$homeHost = parse_url( home_url(), PHP_URL_HOST ); // www.booking.coms
	return preg_replace( '/^www\./', '', $homeHost );  // booking.com
}

/**
 * For local usage only. For global IDs it's better to use function
 * mphb_generate_uid().
 *
 * @return string
 */
function mphb_generate_uuid4(){
	// Source: http://php.net/manual/ru/function.uniqid.php#94959
	$uuid4 = sprintf(
		'%04x%04x%04x%04x%04x%04x%04x%04x'
		, mt_rand( 0, 0xffff )
		, mt_rand( 0, 0xffff )
		, mt_rand( 0, 0xffff )
		, mt_rand( 0, 0x0fff ) | 0x4000
		, mt_rand( 0, 0x3fff ) | 0x8000
		, mt_rand( 0, 0xffff )
		, mt_rand( 0, 0xffff )
		, mt_rand( 0, 0xffff )
	);
	return $uuid4;
}

function mphb_generate_uid(){
	return mphb_generate_uuid4() . '@' . mphb_current_domain();
}

/**
 * Retrieves the edit post link for post regardless current user capabilities
 *
 * @param int|string $id
 * @return string
 */
function mphb_get_edit_post_link_for_everyone( $id, $context = 'display' ){

	if ( !$post = get_post( $id ) ) {
		return '';
	}

	if ( 'revision' === $post->post_type ) {
		$action = '';
	} elseif ( 'display' == $context ) {
		$action = '&amp;action=edit';
	} else {
		$action = '&action=edit';
	}

	$post_type_object = get_post_type_object( $post->post_type );
	if ( !$post_type_object ) {
		return '';
	}

	if ( $post_type_object->_edit_link ) {
		$link = admin_url( sprintf( $post_type_object->_edit_link . $action, $post->ID ) );
	} else {
		$link = '';
	}

	/**
	 * Filters the post edit link.
	 *
	 * @since 2.3.0
	 *
	 * @param string $link The edit link.
	 * @param int $post_id Post ID.
	 * @param string $context The link context. If set to 'display' then ampersands
	 * are encoded.
	 */
	return apply_filters( 'get_edit_post_link', $link, $post->ID, $context );

	return $link;
}

/**
 *
 * @param int $typeId Room type ID.
 *
 * @return array [%Room ID% => %Room Number%].
 */
function mphb_get_rooms_select_list( $typeId ){
	$rooms = MPHB()->getRoomPersistence()->getIdTitleList( array(
		'room_type_id'	 => $typeId,
		'post_status'	 => 'all'
	) );

	$roomType	 = MPHB()->getRoomTypeRepository()->findById( $typeId );
	$typeTitle	 = ( $roomType ? $roomType->getTitle() : '' );

	if ( !empty( $typeTitle ) ) {
		foreach ( $rooms as &$room ) {
			$room	 = str_replace( $typeTitle, '', $room );
			$room	 = trim( $room );
		}
		unset( $room );
	}

	return $rooms;
}

function mphb_show_multiple_instances_notice(){
	/* translators: %s: URL to plugins.php page */
	$message = __( 'You are using two instances of Hotel Booking plugin at the same time, please <a href="%s">deactivate one of them</a>.', 'motopress-hotel-booking' );
	$message = sprintf( $message, esc_url( admin_url( 'plugins.php' ) ) );

	$html_message = sprintf( '<div class="notice notice-warning is-dismissible">%s</div>', wpautop( $message ) );

	echo wp_kses_post( $html_message );
}

/**
 * @param string $wrapper Optional. Wrapper tag - "span" or "div". "span" by
 *     default. Pass the empty value to remove the wrapper
 * @param string $wrapperClass Optional. "description" by default.
 * @return string "Upgrade to Premium..." HTML.
 *
 * @since 3.5.1 parameters $before and $after was replaced with $wrapper and $wrapperClass.
 */
function mphb_upgrade_to_premium_message($wrapper = 'span', $wrapperClass = 'description')
{
	$message = __('<a href="%s">Upgrade to Premium</a> to enable this feature.', 'motopress-hotel-booking');
	$message = sprintf($message, esc_url(admin_url('admin.php?page=mphb_premium')));

    if (!empty($wrapper)) {
        if ($wrapper === 'div') {
            $message = '<div class="' . esc_attr($wrapperClass) . '">' . $message . '</div>';
        } else {
            $message = '<span class="' . esc_attr($wrapperClass) . '">' . $message . '</span>';
        }
    }

	return $message;
}

/**
 * Season price format history:
 * v2.6.0- - single number.
 * v2.7.1- - ["base", "enable_variations" => "0"|"1", "variations" => ""|[["adults", "children", "price"]]].
 * v2.7.2+ - ["periods", "prices", "enable_variations" => true/false, "variations" => [["adults", "children", "prices"]]].
 *
 * @param mixed $price Price in any format.
 *
 * @return array Price in format 2.7.2+.
 */
function mphb_normilize_season_price( $price ){
	$value = array(
		'periods' => array( 1 ),
		'prices'  => array( 0 ),
		'enable_variations' => false,
		'variations' => array()
	);

	if ( !is_numeric( $price ) && !is_array( $price ) ) {
		return $value;
	}

	if ( is_numeric( $price ) ) {
		// Convert v2.6.0- into v2.7.2+
		$value['prices'][0] = $price;

	} else if ( isset( $price['base'] ) ) {
		// Convert v2.7.1- into v2.7.2+
		$value['prices'][0] = $price['base'];
		$value['enable_variations'] = \MPHB\Utils\ValidateUtils::validateBool( $price['enable_variations'] );

	} else {
		// Merge values from v2.7.2+
		$value['periods'] = $price['periods'];
		$value['prices'] = $price['prices'];
		$value['enable_variations'] = $price['enable_variations'];
	}

	// Merge variations
	if ( isset( $price['variations'] ) && is_array( $price['variations'] ) ) {
		foreach ( $price['variations'] as $variation ) {
			if ( isset( $variation['price'] ) ) {
				// Convert v2.7.1- into v2.7.2+
				$prices = array( $variation['price'] );
			} else {
				// Copy prices from v2.7.2+
				$prices = $variation['prices'];
			}

			$value['variations'][] = array(
				'adults'	 => intval( $variation['adults'] ),
				'children'	 => intval( $variation['children'] ),
				'prices'	 => $prices
			);
		}
	}

	return $value;
}

/**
 * Check if term name is reserved.
 *
 * @param  string $termName Term name.
 *
 * @return bool
 *
 * @see https://codex.wordpress.org/Function_Reference/register_taxonomy#Reserved_Terms
 */
function mphb_is_reserved_term( $termName ){
	$reservedTerms = array(
		'attachment',
		'attachment_id',
		'author',
		'author_name',
		'calendar',
		'cat',
		'category',
		'category__and',
		'category__in',
		'category__not_in',
		'category_name',
		'comments_per_page',
		'comments_popup',
		'customize_messenger_channel',
		'customized',
		'cpage',
		'day',
		'debug',
		'error',
		'exact',
		'feed',
		'fields',
		'hour',
		'link_category',
		'm',
		'minute',
		'monthnum',
		'more',
		'name',
		'nav_menu',
		'nonce',
		'nopaging',
		'offset',
		'order',
		'orderby',
		'p',
		'page',
		'page_id',
		'paged',
		'pagename',
		'pb',
		'perm',
		'post',
		'post__in',
		'post__not_in',
		'post_format',
		'post_mime_type',
		'post_status',
		'post_tag',
		'post_type',
		'posts',
		'posts_per_archive_page',
		'posts_per_page',
		'preview',
		'robots',
		's',
		'search',
		'second',
		'sentence',
		'showposts',
		'static',
		'subpost',
		'subpost_id',
		'tag',
		'tag__and',
		'tag__in',
		'tag__not_in',
		'tag_id',
		'tag_slug__and',
		'tag_slug__in',
		'taxonomy',
		'tb',
		'term',
		'theme',
		'type',
		'w',
		'withcomments',
		'withoutcomments',
		'year'
	);

	return in_array( $termName, $reservedTerms, true );
}

/**
 * @param string $haystack
 * @param string $needle
 * @return bool
 *
 * @since 3.7.0
 *
 * @author MrHus
 * @link http://stackoverflow.com/a/834355/3918377
 */
function mphb_string_starts_with($haystack, $needle)
{
    $length = strlen($needle);
    return (substr($haystack, 0, $length) === $needle);
}

/**
 * @param string $haystack
 * @param string $needle
 *
 * @return bool
 *
 * @author MrHus
 *
 * @see https://stackoverflow.com/a/834355/3918377
 */
function mphb_string_ends_with( $haystack, $needle ){
    $length = strlen( $needle );

    if ( $length == 0 ) {
        return true;
    }

    return ( substr( $haystack, -$length ) === $needle );
}

/**
 * @since 3.0
 */
function mphb_array_disjunction( $a, $b ){
    return array_merge( array_diff( $a, $b ), array_diff( $b, $a ) );
}

/**
 * @return array "publish", and maybe "private", if current user can read
 * private posts.
 *
 * @since 3.0.1
 */
function mphb_readable_post_statuses(){
	if ( current_user_can( 'read_private_posts' ) ) {
		return array( 'publish', 'private' );
	} else {
		return array( 'publish' );
	}
}

/**
 * @since 3.0.2
 */
function mphb_db_version()
{
    // Min version "1.0.1" can be found in the upgrader constants
    return get_option('mphb_db_version', '1.0.1');
}

/**
 * @since 3.0.2
 */
function mphb_db_version_at_least($requiredVersion)
{
    $dbVersion = mphb_db_version();
    return version_compare($dbVersion, $requiredVersion, '>=');
}

/**
 * @since 3.0.3
 */
function mphb_version_at_least($requiredVersion)
{
    $actualVersion = MPHB()->getVersion();
    return version_compare($actualVersion, $requiredVersion, '>=');
}

/**
 * @param string $requiredVersion
 * @return bool
 *
 * @global string $wp_version
 *
 * @since 3.7.4
 */
function mphb_wordpress_at_least($requiredVersion)
{
    global $wp_version;

    return version_compare($wp_version, $requiredVersion, '>=');
}

/**
 * @see Issue ticket in WordPress Trac: https://core.trac.wordpress.org/ticket/45495
 *
 * @since 3.3.0
 */
function mphb_fix_blocks_autop()
{
    if (mphb_wordpress_at_least('5.2')) {
        // The bug was fixed since WP 5.2
        return;

    } else if (mphb_wordpress_at_least('5.0') && has_filter('the_content', 'wpautop') !== false) {
        remove_filter('the_content', 'wpautop');
        add_filter('the_content', function ($content) {
            if (has_blocks()) {
                return $content;
            }

            return wpautop($content);
        });
    }
}

/**
 * @param string $json JSON string with possibly escaped Unicode symbols (\uXXXX).
 * @return string JSON string with escaped Unicode symbols (\\uXXXX).
 *
 * @since 3.5.0
 */
function mphb_escape_json_unicodes($json)
{
    return preg_replace('/(\\\\u[0-9a-f]{4})/i', '\\\\$1', $json);
}

/**
 * @return string "/path/to/wordpress/wp-content/uploads/mphb/"
 *
 * @since 3.5.0
 */
function mphb_uploads_dir()
{
    $uploads = wp_upload_dir();
    return trailingslashit($uploads['basedir']) . 'mphb/';
}

/**
 * @since 3.5.0
 */
function mphb_create_uploads_dir()
{
	$dir = mphb_uploads_dir();

	if ( file_exists( $dir ) ) {
        return;
    }

    // Create .../uploads/mphb/
    wp_mkdir_p( $dir );

	require_once( ABSPATH . 'wp-admin/includes/class-wp-filesystem-base.php' );
	require_once( ABSPATH . 'wp-admin/includes/class-wp-filesystem-direct.php' );

	if ( !class_exists( 'WP_Filesystem_Direct' ) ) {
		return;
	}

	$fileSystemDirect = new WP_Filesystem_Direct( null );

	$source = MPHB()->getPluginDir() . '/assets/others/index.php';

	$destination = $dir . 'index.php';

	$fileSystemDirect->copy( $source, $destination, true );

	$source = MPHB()->getPluginDir() . '/assets/others/.htaccess';

	$destination = $dir . '.htaccess';

	$fileSystemDirect->copy( $source, $destination, true );
}

/**
 * @since 3.6.0
 */
function mphb_verify_nonce( $action, $nonceName = 'mphb_nonce' )
{
    if (!isset($_REQUEST[ $nonceName ])) {
        return false;
    }

    $nonce = sanitize_text_field( wp_unslash( $_REQUEST[ $nonceName ] ) );

    return wp_verify_nonce( $nonce, $action );
}

/**
 * @since 3.7.1
 */
function mphb_get_polyfill_for($function)
{
    switch ($function) {
        case 'mb_convert_encoding': require_once MPHB()->getPluginPath('includes/polyfills/mbstring.php'); break;
    }
}

/**
 * @return int
 *
 * @since 3.7.2
 */
function mphb_current_year()
{
    return intval(strftime('%Y'));
}

/**
 * @param int $month
 * @param int $year
 * @return int
 *
 * @since 3.7.2
 */
function mphb_days_in_month($month, $year)
{
    return cal_days_in_month(CAL_GREGORIAN, $month, $year);
}

/**
 * @return array
 *
 * @since 3.7.2
 */
function mphb_get_customer_fields()
{
    return MPHB()->settings()->main()->getCustomerBundle()->getCustomerFields();
}

/**
 * @return array
 *
 * @since 3.7.2
 */
function mphb_get_default_customer_fields()
{
    return MPHB()->settings()->main()->getCustomerBundle()->getDefaultFields();
}

/**
 * @param string $fieldName
 * @return bool
 *
 * @since 3.7.2
 */
function mphb_is_default_customer_field($fieldName)
{
    return MPHB()->settings()->main()->getCustomerBundle()->isDefaultField($fieldName);
}

/**
 * @return array
 *
 * @since 3.7.2
 */
function mphb_get_custom_customer_fields()
{
    return MPHB()->settings()->main()->getCustomerBundle()->getCustomFields();
}

/**
 * @return array
 *
 * @since 3.7.2
 */
function mphb_get_admin_checkout_customer_fields()
{
    return MPHB()->settings()->main()->getCustomerBundle()->getAdminCheckoutFields();
}

/**
 * @return int
 *
 * @since 3.7.2
 */
function mphb_get_editing_post_id()
{
    $postId = 0;

    if (is_admin()) {
        if (isset($_REQUEST['post_ID']) && is_numeric($_REQUEST['post_ID'])) {
            $postId = intval($_REQUEST['post_ID']); // On post update ($_POST)
        } else if (isset($_REQUEST['post']) && is_numeric($_REQUEST['post'])) {
            $postId = intval($_REQUEST['post']); // On post edit page ($_GET)
        }
    }

    return $postId;
}

/**
 * @param int|float $value
 * @param int|float $min
 * @param int|float $max
 * @return int|float
 *
 * @since 3.7.2
 */
function mphb_limit($value, $min, $max)
{
    return max($min, min($value, $max));
}

/**
 * Add an array after the specified position.
 *
 * @param array $array Subject array.
 * @param int $position
 * @param array $insert Array to insert.
 * @return array Result array with inserted items.
 *
 * @since 3.7.2
 */
function mphb_array_insert_after($array, $position, $insert)
{
    if ($position < 0) {
        return array_merge($insert, $array);
    } else if ($position >= count($array)) {
        return array_merge($array, $insert);
    } else {
        return array_merge(
            array_slice($array, 0, $position + 1, true),
            $insert,
            array_slice($array, $position + 1, count($array), true)
        );
    }
}

/**
 * Add an array after the specified key in the associative array.
 *
 * @param array $array Subject array.
 * @param mixed $searchKey
 * @param array $insert Array to insert.
 * @return array Result array with inserted items.
 *
 * @since 3.7.2
 */
function mphb_array_insert_after_key($array, $searchKey, $insert)
{
    $position = array_search($searchKey, array_keys($array));

    if ($position !== false) {
        return mphb_array_insert_after($array, $position, $insert);
    } else {
        return mphb_array_insert_after($array, count($array), $insert);
    }
}

/**
 * @param array $haystack
 * @param callable $checkCallback The callback check function. The function must
 *     return TRUE if the proper element was found or FALSE otherwise. Gets the
 *     value of the element as the first argument and the key as second.
 * @return mixed The key for searched element or FALSE.
 *
 * @since 3.7.2
 */
function mphb_array_usearch(array $haystack, callable $checkCallback)
{
    foreach ($haystack as $key => $value) {
        if ($checkCallback($value, $key)) {
            return $key;
        }
    }

    return false;
}

/**
 * @param string $str
 * @param string $separator Optional. "_" by default.
 * @return string
 *
 * @since 3.7.3
 */
function mphb_prefix($str, $separator = '_')
{
    return MPHB()->addPrefix($str, $separator);
}

/**
 * @param string $str
 * @param string $separator Optional. "_" by default.
 * @return string
 *
 * @since 3.7.3
 */
function mphb_unprefix($str, $separator = '_')
{
    $prefix = MPHB()->getPrefix() . $separator;
    return str_replace($prefix, '', $str);
}

/**
 * @param int $bookingId
 * @param bool $force Optional. FALSE by default.
 * @return \MPHB\Entities\Booking|null
 *
 * @since 3.7.3
 */
function mphb_get_booking($bookingId, $force = false)
{
    return MPHB()->getBookingRepository()->findById($bookingId, $force);
}

/**
 * @param int $bookingId
 * @return \MPHB\Entities\Customer|null
 *
 * @since 3.7.3
 */
function mphb_get_customer($bookingId)
{
    $booking = mphb_get_booking($bookingId);

    if (!is_null($booking)) {
        return $booking->getCustomer();
    } else {
        return null;
    }
}

/**
 * @param int $roomTypeId
 * @param bool $force Optional. FALSE by default.
 * @return \MPHB\Entities\RoomType|null
 *
 * @since 3.8
 */
function mphb_get_room_type($roomTypeId, $force = false)
{
    return MPHB()->getRoomTypeRepository()->findById($roomTypeId, $force);
}

/**
 * @param int $seasonId
 * @param bool $force Optional. False by default.
 * @return \MPHB\Entities\Season|null
 *
 * @since 3.9
 */
function mphb_get_season($seasonId, $force = false)
{
    return MPHB()->getSeasonRepository()->findById($seasonId, $force);
}

/**
 * Determine if the current view is the "All" view.
 *
 * @see \WP_Posts_List_Table::is_base_request()
 *
 * @param string|null $postType Optional. NULL by default.
 * @return bool
 *
 * @global string $typenow
 *
 * @since 3.7.3
 */
function mphb_is_base_request($postType = null)
{
    global $typenow;

    $allowedVars = [
        'post_type' => true,
        'paged'     => true,
        'all_posts' => true
    ];

    $unallowedVars = array_diff_key($_GET, $allowedVars);

    $isBase = count($unallowedVars) == 0;

    // Add additional check of the post type
    if (!is_null($postType) && $isBase) {
        $isBase = $postType === $typenow;
    }

    return $isBase;
}

/**
 * @param \MPHB\Entities\Booking $booking
 * @return bool
 *
 * @since 3.7.6
 */
function mphb_is_complete_booking($booking)
{
    $bookedStatuses = MPHB()->postTypes()->booking()->statuses()->getBookedRoomStatuses();
    return in_array($booking->getStatus(), $bookedStatuses);
}

/**
 * @param \MPHB\Entities\Booking $booking
 * @return bool
 *
 * @since 3.7.6
 */
function mphb_is_pending_booking($booking)
{
    $pendingStatuses = MPHB()->postTypes()->booking()->statuses()->getPendingRoomStatuses();
    return in_array($booking->getStatus(), $pendingStatuses);
}

/**
 * @param \MPHB\Entities\Booking $booking
 * @return bool
 *
 * @since 3.7.6
 */
function mphb_is_locking_booking($booking)
{
    $lockingStatuses = MPHB()->postTypes()->booking()->statuses()->getLockedRoomStatuses();
    return in_array($booking->getStatus(), $lockingStatuses);
}

/**
 * @param \MPHB\Entities\Booking $booking
 * @return bool
 *
 * @since 3.7.6
 */
function mphb_is_failed_booking($booking)
{
    $failedStatuses = MPHB()->postTypes()->booking()->statuses()->getFailedStatuses();
    return in_array($booking->getStatus(), $failedStatuses);
}

/**
 * @param \DateTime $from Start date, like check-in date.
 * @param \DateTime $to End date, like check-out date.
 * @param array $atts Optional.
 *     @param int $atts['room_type_id'] Optional. 0 by default (any room type).
 *     @param int|int[] $atts['exclude_bookings'] Optional. One or more booking IDs.
 * @return array [Room type ID => [Rooms IDs]] (all IDs - original)
 *
 * @since 3.8
 */
function mphb_get_available_rooms($from, $to, $atts = array())
{
		$roomTypeId = isset($atts['room_type_id']) ? $atts['room_type_id'] : 0;
		$searchAtts = array();

		if (isset($atts['exclude_bookings'])) {
				$searchAtts['exclude_bookings'] = $atts['exclude_bookings'];
		}

		$searchAtts['skip_buffer_rules'] = false;

		return MPHB()->getRoomRepository()->getAvailableRooms($from, $to, $roomTypeId, $searchAtts);
}

/**
 * @param int|string $value
 * @return int The number in range [0; oo)
 *
 * @since 3.8
 */
function mphb_posint($value)
{
    return max(0, intval($value));
}

/**
 * @return int
 *
 * @since 3.8
 */
function mphb_get_min_adults()
{
    return MPHB()->settings()->main()->getMinAdults();
}

/**
 * @return int
 *
 * @since 3.8
 */
function mphb_get_min_children()
{
    return MPHB()->settings()->main()->getMinChildren();
}

/**
 * @return int
 *
 * @since 3.8
 */
function mphb_get_max_adults()
{
    return MPHB()->settings()->main()->getSearchMaxAdults();
}

/**
 * @return int
 *
 * @since 3.8
 */
function mphb_get_max_children()
{
    return MPHB()->settings()->main()->getSearchMaxChildren();
}

/**
 * @param array $array Array to flip.
 * @param bool $arraySingle Optional. Convert single value into array. FALSE by default.
 * @return array
 *
 * @since 3.8
 */
function mphb_array_flip_duplicates($array, $arraySingle = false)
{
    $values = array_unique($array);
    $flip = array();

    foreach ($values as $value) {
        $keys = array_keys($array, $value);

        if ($arraySingle || count($keys) > 1) {
            $flip[$value] = $keys;
        } else {
            $flip[$value] = reset($keys);
        }
    }

    return $flip;
}

/**
 * @param \MPHB\Entities\RoomType|int|null Optional. Room type or it's ID.
 *     Current room type by default.
 * @return float
 *
 * @since 3.8.3
 */
function mphb_get_room_type_base_price($roomType = null)
{
    if (is_null($roomType)) {
        $roomType = MPHB()->getCurrentRoomType();
    } else if (is_int($roomType)) {
        $roomType = mphb_get_room_type($roomType);
    }

    if (is_null($roomType)) {
        return 0.0;
    }

    $price = 0.0;
    $rates = MPHB()->getRateRepository()->findAllActiveByRoomType($roomType->getOriginalId());

    if (!empty($rates)) {
        $startDate = new \DateTime();
        $endDate = DateUtils::cloneModify($startDate, sprintf('+%d days', MPHB()->settings()->main()->getAveragePricePeriod()));

        $prices = array_map(
            function ($rate) use ($startDate, $endDate) {
                return $rate->getMinBasePrice($startDate, $endDate);
            },
            $rates
        );
        $prices = array_filter($prices);

        if (!empty($prices)) {
            $price = min($prices);
        }
    }

    return $price;
}

/**
 * @param \DateTime $startDate
 * @param \DateTime $endDate
 * @param \MPHB\Entities\RoomType|int|null Optional. Room type or it's ID.
 *     Current room type by default.
 * @param array $args Optional.
 * @param int $args['adults']
 * @param int $args['children']
 * @return float
 *
 * @since 3.8.3
 */
function mphb_get_room_type_period_price($startDate, $endDate, $roomType = null, $args = array())
{
    if (is_null($roomType)) {
        $roomType = MPHB()->getCurrentRoomType();
    } else if (is_int($roomType)) {
        $roomType = mphb_get_room_type($roomType);
    }

    if (is_null($roomType)) {
        return 0.0;
    }

    $price = 0.0;

    $rates = MPHB()->getRateRepository()->findAllActiveByRoomType(
        $roomType->getOriginalId(),
        array(
            'check_in_date'  => $startDate,
            'check_out_date' => $endDate
        )
    );

    if (!empty($rates)) {
        $searchArgs = array(
            'check_in_date'  => $startDate,
            'check_out_date' => $endDate
        );

        if (isset($args['adults']) || isset($args['children'])) {
            $searchArgs['adults']   = isset($args['adults'])   ? $args['adults']   : MPHB()->settings()->main()->getMinAdults();
            $searchArgs['children'] = isset($args['children']) ? $args['children'] : MPHB()->settings()->main()->getMinChildren();
        }

        MPHB()->reservationRequest()->setupSearchParameters()->setupParameters($searchArgs);

        $prices = array_map(
            function ($rate) use ($startDate, $endDate) {
                return $rate->calcPrice($startDate, $endDate);
            },
            $rates
        );
        $prices = array_filter($prices);

        if (!empty($prices)) {
            $price = min($prices);
        }



        MPHB()->reservationRequest()->resetDefaults();
    }

    return $price;
}

/**
 * @param 'any'|'original' $language Optional. 'any' by default.
 * @param array $atts Additional atts.
 * @return int[]
 *
 * @since 3.9
 */
function mphb_get_room_type_ids($language = 'any', $atts = array())
{
    if ($language == 'original') {
        // Multilingual support
        $atts['mphb_language'] = 'original';
    }

    $atts['fields'] = 'ids'; // Force IDs

    return MPHB()->getRoomTypePersistence()->getPosts($atts);
}

/**
 * @param string $modifier Optional. Modifier like '+1 day'. Empty by default.
 * @return DateTime
 *
 * @since 3.9
 */
function mphb_today($modifier = '')
{
    $date = new DateTime('today');

    if (!empty($modifier)) {
        $date->modify($modifier);
    }

    return $date;
}

/**
 * @return bool
 *
 * @since 3.9
 */
function mphb_has_buffer_days()
{
	return MPHB()->getRulesChecker()->bufferRules()->hasRules();
}

/**
 * @param DateTime $date
 * @param int $roomTypeId
 * @return int Buffer days amount.
 *
 * @since 3.9
 */
function mphb_get_buffer_days($date, $roomTypeId = 0)
{
	$actualRule = MPHB()->getRulesChecker()->bufferRules()->findActualRule($date, $roomTypeId);

	return $actualRule->getBufferDays();
}

/**
 * @param DateTime $startDate
 * @param DateTime $endDate
 * @param int $bufferDays
 * @return DateTime[] [0 => Modified start date, 1 => Modified end date]
 *
 * @since 3.9
 */
function mphb_modify_buffer_period( $startDate, $endDate, $bufferDays = 0 )
{
		if( $bufferDays > 0 ) {
			$beforeStart = DateUtils::cloneModify($startDate, "-{$bufferDays} days");
			$afterEnd    = DateUtils::cloneModify($endDate, "+{$bufferDays} days");

			return array( $beforeStart, $afterEnd );
		}

	return array( $startDate, $endDate );
}

/**
 * @param MPHB\Entities\Booking $booking
 * @param int $bufferDays
 * @param bool $extendDates Optional. Extend the final result with check-in and
 *     next to the check-out dates. Useful for Booking Calendar, for example.
 *     False by default.
 * @return DateTime[] [Date string ('Y-m-d') => Date object] - only the dates of
 *     the buffer.
 *
 * @since 3.9
 */
function mphb_modify_booking_buffer_period($booking, $bufferDays, $extendDates = false)
{
	$checkInDate = $booking->getCheckInDate();
	$checkOutDate = $booking->getCheckOutDate();

	$offsetDays = $extendDates ? $bufferDays + 1 : $bufferDays;

	// Find the buffer range
	list($beforeCheckIn, $afterCheckOut) = mphb_modify_buffer_period($checkInDate, $checkOutDate, $bufferDays);

	if ($extendDates) {
		$afterCheckOut->modify('+1 day');
	}

	// Build the full period
	$fullPeriod = DateUtils::createDatePeriod($beforeCheckIn, $afterCheckOut);

	// Split period to dates
	$bufferDates = iterator_to_array($fullPeriod);
	array_splice($bufferDates, $offsetDays, -$offsetDays); // Remove booking inner dates

	$dateFormat  = MPHB()->settings()->dateTime()->getDateTransferFormat();
	$dateStrings = array_map(function ($date) use ($dateFormat) { return $date->format($dateFormat); }, $bufferDates);

	return array_combine($dateStrings, $bufferDates);
}

/**
 * @param array $bookingPeriod
 * @param int $roomTypeId
 *
 * @return DateTime[] [0 => Modified start date, 1 => Modified end date]
 *
 * @since 3.9
 */
function mphb_modify_booking_period_with_booking_buffer( $bookingPeriod, $roomTypeId ) {
		$bufferDays = mphb_get_buffer_days( $bookingPeriod[0], $roomTypeId );

		return mphb_modify_buffer_period( $bookingPeriod[0], $bookingPeriod[1], $bufferDays );
}
add_filter( 'mphb_modify_booking_period', 'mphb_modify_booking_period_with_booking_buffer', 10, 2 );

/**
 * @param array $atts
 *
 * @return array
 *
 * @since 3.9
 */
function mphb_is_rooms_free_query_atts_with_buffer( $atts ) {
		$atts['skip_buffer_rules'] = false;

		return $atts;
}
add_filter( 'mphb_is_rooms_free_query_atts', 'mphb_is_rooms_free_query_atts_with_buffer' );

/**
 * @param array $datesArray
 * @param int $roomsTotal
 *
 * @return array [Date string ('Y-m-d') => int]
 *
 * @since 3.9
 */
function mphb_filter_checkin_checkout_dates( $datesArray, $roomsTotal ) {

	$checkIns = array();
	$checkOuts = array();

	foreach( $datesArray as $date => $roomsCount ) {

		if( $roomsCount >= $roomsTotal ) {

			$dateObj = \DateTime::createFromFormat( 'Y-m-d', $date );
			$dateYesterday = MPHB\Utils\DateUtils::cloneModify( \DateTime::createFromFormat( 'Y-m-d', $date ), "-1 day" )->format( "Y-m-d" );
			$dateTomorrow = MPHB\Utils\DateUtils::cloneModify( \DateTime::createFromFormat( 'Y-m-d', $date ), "+1 day" )->format( "Y-m-d" );

			if( empty( $checkIns ) ) {
				$checkIns[$date] = $roomsCount;
				$checkOuts[$dateTomorrow] = $roomsCount;
			} else {
				if( !isset( $datesArray[$dateYesterday] ) || $datesArray[$dateYesterday] < $roomsCount ) {
					$checkIns[$date] = $roomsCount;
					$checkOuts[$dateTomorrow] = $roomsCount;
				} else {
					if( !empty( $checkOuts ) ) {
						$lastCheckOutDate = array_keys( $checkOuts )[count( $checkOuts ) - 1];
						unset( $checkOuts[$lastCheckOutDate] );
						$checkOuts[$dateTomorrow] = $roomsCount;
					}
				}
			}
		}

	}

	return array( $checkIns, $checkOuts );

}

/**
 * Display a help tip.
 *
 * @param  string $tip        Help tip text.
 * @param  bool   $allow_html Allow sanitized HTML if true or escape.
 * @return string
 *
 * @since  3.9.8
 */
function mphb_help_tip( $tip, $allow_html = false ) {
	if ( $allow_html ) {
		$tip = wp_kses( $tip, wp_kses_allowed_html( 'post' ) );
	} else {
		$tip = esc_attr( $tip );
	}

	return '<span class="mphb-help-tip" data-tip="' . $tip . '"></span>';
}

/**
 *
 * @param  string $endpoint  Endpoint slug.
 * @param  string $value     Query param value.
 * @param  string $permalink Permalink.
 * 
 * @since 4.2.0
 *
 * @return string
 */
function mphb_create_url( $endpoint, $value = '', $permalink = '' ) {
	global $wp;
	
	if ( ! $permalink ) {
		$permalink = get_permalink();
	}

	$query_vars = $wp->query_vars;
	$endpoint   = ! empty( $query_vars[ $endpoint ] ) ? $query_vars[ $endpoint ] : $endpoint;
	
	if ( get_option( 'permalink_structure' ) ) {
		if ( strstr( $permalink, '?' ) ) {
			$query_string = '?' . wp_parse_url( $permalink, PHP_URL_QUERY );
			$permalink    = current( explode( '?', $permalink ) );
		} else {
			$query_string = '';
		}
		$url = trailingslashit( $permalink );

		if ( $value ) {
			$url .= trailingslashit( $endpoint ) . user_trailingslashit( $value );
		} else {
			$url .= user_trailingslashit( $endpoint );
		}

		$url .= $query_string;
	} else {
		$url = add_query_arg( $endpoint, $value, $permalink );
	}

	return $url;
}