<?php

/**
 * @package Pods\Global\Functions\General
 */

use Pods\Admin\Settings;
use Pods\API\Whatsit\Value_Field;
use Pods\Config_Handler;
use Pods\Container\Container_DI52;
use Pods\Permissions;
use Pods\Data\Map_Field_Values;
use Pods\Whatsit;
use Pods\Whatsit\Field;
use Pods\Whatsit\Object_Field;
use Pods\Whatsit\Pod;
use Pods\Whatsit\Store;

/**
 * Standardize queries and error reporting. It replaces @wp_ with $wpdb->prefix.
 *
 * @see   PodsData::query
 *
 * @param string|array $sql              The SQL query or an array with the SQL query and the values to prepare.
 * @param string       $error            (optional) The failure message to use for Database errors.
 * @param string       $results_error    (optional) Throw an error if a records are found.
 * @param string       $no_results_error (optional) Throw an error if no records are found.
 *
 * @return array|bool|mixed|null|void
 * @since 2.0.0
 */
function pods_query( $sql, $error = 'Database Error', $results_error = null, $no_results_error = null ) {
	try {
		$podsdata = pods_data( null, null, true, false );
	} catch ( Exception $exception ) {
		return null;
	}

	// If the $error is the $prepare array, set the $error to the default message.
	if ( is_array( $error ) ) {
		if ( ! is_array( $sql ) ) {
			$sql = array( $sql, $error );
		}

		$error = 'Database Error';
	}

	if ( is_array( $sql ) ) {
		$sql = array_values( $sql );

		$sql[0] = apply_filters( 'pods_query_sql', $sql[0], $error, $results_error, $no_results_error );
		$sql[0] = $podsdata->get_sql( $sql[0] );
	} else {
		$sql = apply_filters( 'pods_query_sql', $sql, $error, $results_error, $no_results_error );
		$sql = $podsdata->get_sql( $sql );
	}

	if ( 1 === (int) pods_v( 'pods_debug_sql_all' ) && is_user_logged_in() && pods_is_admin( array( 'pods' ) ) ) {
		$debug_sql = $sql;

		if ( is_array( $debug_sql ) ) {
			$debug_sql = print_r( $debug_sql, true );
		}

		echo '<textarea cols="100" rows="24">';
		echo esc_textarea( $debug_sql );
		echo '</textarea>';
	}

	return PodsData::query( $sql, $error, $results_error, $no_results_error );
}

/**
 * Prepare and run the query.
 *
 * @since 2.8.22
 *
 * @see   PodsData::query
 *
 * @param string $sql              SQL Query
 * @param array  $prepare          Variables to prepare for the SQL query.
 * @param string $error            (optional) The failure message
 * @param string $results_error    (optional) Throw an error if a records are found
 * @param string $no_results_error (optional) Throw an error if no records are found
 *
 * @return array|bool|mixed|null|void
 */
function pods_query_prepare( $sql, $prepare, $error = 'Database Error', $results_error = null, $no_results_error = null ) {
	return pods_query( [ $sql, $prepare ], $error, $results_error, $no_results_error );
}

/**
 * Standardize filters / actions
 *
 * @param string $scope Scope of the filter / action (ui for PodsUI, api for PodsAPI, etc..)
 * @param string $name  Name of filter / action to run
 * @param mixed  $args  (optional) Arguments to send to filter / action
 * @param object $obj   (optional) Object to reference for filter / action
 *
 * @return mixed
 * @since 2.0.0
 * @todo  Need to figure out how to handle $scope = 'pods' for the Pods class
 */
function pods_do_hook( $scope, $name, $args = null, $obj = null ) {
	// Add filter name
	array_unshift( $args, "pods_{$scope}_{$name}" );

	// Add object
	$args[] = $obj;

	// Run apply_filters and give it all the arguments
	$args = call_user_func_array( 'apply_filters', $args );

	return $args;
}

/**
 * Message / Notice handling for Admin UI
 *
 * @param string $message        The notice / error message shown.
 * @param string $type           The message type (success, info, warning, error, or nag).
 * @param bool   $return         Whether to return the message.
 * @param bool   $is_dismissible Whether the notice is dismissible.
 *
 * @return string|null The message or null if not returning.
 */
function pods_message( $message, $type = null, $return = false, $is_dismissible = true ) {
	$message = (string) $message;

	$supported_notice_types = [
		'success',
		'info',
		'warning',
		'error',
	];

	// The "nag" type will be treated as "info", but we want to check to see if the constant has it turned off.
	if ( 'nag' === $type && defined( 'DISABLE_NAG_NOTICES' ) && DISABLE_NAG_NOTICES ) {
		if ( $return ) {
			return '';
		}

		return null;
	}

	if ( empty( $type ) || ! in_array( $type, $supported_notice_types, true ) ) {
		$type = 'info';
	}

	// Maybe handle WP-CLI messages.
	if ( defined( 'WP_CLI' ) ) {
		if ( 'error' === $type ) {
			WP_CLI::warning( $message );
		} else {
			WP_CLI::line( $message );
		}

		if ( $return ) {
			return '';
		}

		return null;
	}

	// Maybe wrap the message.
	if ( false === strpos( $message, '</p>' ) ) {
		$message = '<p>' . $message . '</p>';
	}

	$div_id = 'message';

	$div_classes = [
		'notice',
		'notice-' . $type,
	];

	if ( $is_dismissible ) {
		$div_classes[] = 'is-dismissible';
	}

	$div_classes = array_filter( $div_classes );

	// Maybe prefix the id/classes on frontend.
	if ( ! is_admin() ) {
		$div_classes = array_map(
			static function( $class_name ) {
				return 'pods-ui-notice-' . $class_name;
			},
			$div_classes
		);

		$div_classes[] = 'pods-ui-notice-front';

		// No ID here since there may be multiple on the page.
		$div_attr_id = '';

		wp_enqueue_style( 'pods-form' );
	} else {
		$div_attr_id = 'id="' . esc_attr( $div_id ) . '"';
	}

	$div_classes = implode( ' ', $div_classes );

	$html = '<div ' . $div_attr_id . ' class="pods-ui-notice ' . esc_attr( $div_classes ) . '">' . $message . '</div>';

	if ( $return ) {
		return $html;
	}

	echo $html;

	return null;
}

$GLOBALS['pods_errors'] = array();

/**
 * The default exception handler for Pods errors.
 *
 * @since 2.9.4
 *
 * @param string|array $error The error message(s) to be thrown / displayed.
 */
function pods_error_exception( $error ) {
	pods_error( $error, 'final_exception' );
}

/**
 * Error Handling which throws / displays errors
 *
 * @param string|array        $error The error message(s) to be thrown / displayed.
 * @param object|boolean|null $obj   If $obj->display_errors is set and is set to true it will display errors,
 *                                   if boolean and is set to true it will display errors.
 *
 * @return mixed
 *
 * @throws Exception Throws exception for developer-oriented error handling.
 *
 * @since 2.0.0
 */
function pods_error( $error, $obj = null ) {
	global $pods_errors;

	$display_errors = $obj;
	if ( is_object( $obj ) && isset( $obj->display_errors ) ) {
		$display_errors = $obj->display_errors;
	}

	$error_mode = 'exception';

	if ( true === $display_errors ) {
		$error_mode = 'exit';
	} elseif ( false === $display_errors ) {
		$error_mode = 'exception';
	} elseif ( is_string( $display_errors ) ) {
		$error_mode = $display_errors;
	}

	if ( is_object( $error ) && 'Exception' === get_class( $error ) ) {
		$error_mode = 'exception';

		if ( 'final_exception' === $display_errors ) {
			$error_mode = 'exit';
		}

		/** @var Exception $error */
		$error = $error->getMessage();
	}

	/**
	 * @var string $error_mode Throw an exception, exit with the message, return false, or return WP_Error
	 */
	if ( ! in_array( $error_mode, array( 'exception', 'exit', 'false', 'wp_error', 'json' ), true ) ) {
		$error_mode = 'exception';
	}

	/**
	 * When running a Pods shortcode, never exit and only return exception.
	 */
	if ( pods_doing_shortcode() ) {
		$error_mode = 'exception';
	} elseif ( pods_doing_json() ) {
		$error_mode = 'json';
	}

	/**
	 * Filter the error mode used by pods_error.
	 *
	 * @param string                     $error_mode Error mode
	 * @param string|array               $error      Error message(s)
	 * @param object|boolean|string|null $obj
	 */
	$error_mode = apply_filters( 'pods_error_mode', $error_mode, $error, $obj );

	if ( is_array( $error ) ) {
		$error = array_map( 'wp_kses_post', $error );

		if ( 1 === count( $error ) ) {
			$error = current( $error );

			// Create WP_Error for use later.
			$wp_error = new WP_Error( 'pods-error-' . md5( $error ), $error );
		} else {
			// Create WP_Error for use later.
			$wp_error = new WP_Error();

			foreach ( $error as $error_message ) {
				$wp_error->add( 'pods-error-' . md5( $error_message ), $error_message );
			}

			if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
				$error = __( 'The following issue occurred:', 'pods' ) . "\n\n- " . implode( "\n- ", $error );
			} else {
				$error = __( 'The following issues occurred:', 'pods' ) . "\n<ul><li>" . implode( "</li>\n<li>", $error ) . '</li></ul>';
			}
		}
	} else {
		if ( is_object( $error ) ) {
			$error = __( 'An unknown error has occurred', 'pods' );
		}

		$error = wp_kses_post( $error );

		// Create WP_Error for use later.
		$wp_error = new WP_Error( 'pods-error-' . md5( $error ), $error );
	}//end if

	$pods_errors = array();

	// Support testing debug messages.
	if ( function_exists( 'codecept_debug' ) ) {
		codecept_debug( 'Pods Debug Error: ' . $error );
		pods_debug( debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS ) );
	}

	if ( ! empty( $error ) ) {
		if ( 'exception' === $error_mode ) {
			$exception_bypass = apply_filters( 'pods_error_exception', null, $error );

			if ( null !== $exception_bypass ) {
				return $exception_bypass;
			}

			$pods_errors = $error;

			/**
			 * Allow filtering whether the fallback is enabled to catch uncaught exceptions.
			 *
			 * @since 2.8.0
			 *
			 * @param bool   $exception_fallback_enabled Whether the fallback is enabled to catch uncaught exceptions.
			 * @param string $error                      The error information.
			 */
			$exception_fallback_enabled = apply_filters( 'pods_error_exception_fallback_enabled', true, $error );

			if ( $exception_fallback_enabled ) {
				set_exception_handler( 'pods_error_exception' );
			}

			throw new Exception( $error );
		} elseif ( 'exit' === $error_mode ) {
			$die_bypass = apply_filters( 'pods_error_die', null, $error );

			if ( null !== $die_bypass ) {
				return $die_bypass;
			}

			// die with error
			if ( ! defined( 'DOING_AJAX' ) && ! headers_sent() && ( is_admin() || false !== strpos( $_SERVER['REQUEST_URI'], 'wp-comments-post.php' ) ) ) {
				wp_die( $error, '', array( 'back_link' => true ) );
			} else {
				die( sprintf( '<e>%s</e>', $error ) );
			}
		} elseif ( 'wp_error' === $error_mode ) {
			return $wp_error;
		} elseif ( 'json' === $error_mode ) {
			$meta_box_loader_compat = (int) pods_v( 'meta-box-loader', 'request', 0 );

			// Check if this is a back-compat meta box save request.
			if ( 1 === $meta_box_loader_compat ) {
				// Do not block this page.
				error_log( 'Pods Meta Save Error:' . $error );
			} else {
				wp_send_json( [
					'message' => $error,
				], 500 );
			}
		}//end if
	}//end if

	return false;
}

/**
 * Get the last known timing difference.
 *
 * @since 2.9.10
 *
 * @return float The last known timing difference.
 */
function pods_get_timing() {
	static $timer;

	$now = microtime( true );

	if ( ! $timer ) {
		$timer = $now;
	}

	$last_diff = $now - $timer;

	$timer = $now;

	return $last_diff;
}

/**
 * Get the last known timing difference as text.
 *
 * @since 2.9.10
 *
 * @return string The last known timing difference as text.
 */
function pods_get_debug_timing() {
	return '[debug timing: ' . number_format( pods_get_timing(), 4 ) . 's]';
}

/**
 * Debug variable used in pods_debug to count the instances debug is used
 */
global $pods_debug;
$pods_debug = 0;

/**
 * Output information about a variable using var_dump() to save a few lines and is compatible with Xdebug and WP-CLI.
 *
 * @since 2.0.0
 *
 * @param mixed   $debug  The error message to be thrown / displayed.
 * @param bool    $die    If set to true, a die() will occur, if set to (int) 2 then a wp_die() will occur.
 * @param string  $prefix Extra information to output above the debug information.
 */
function pods_debug( $debug = '_null', $die = false, $prefix = '_null' ) {
	global $pods_debug;

	$pods_debug ++;

	if ( function_exists( 'codecept_debug' ) ) {
		if ( $debug instanceof Exception ) {
			$debug = 'Exception bypassed: [' . $debug->getCode() . '] ' . $debug->getMessage();
		} elseif ( ! is_string( $debug ) ) {
			$debug = var_export( $debug, true );
		}

		if ( '_null' === $prefix ) {
			$prefix = 'Pods Debug';
		}

		codecept_debug( $prefix . ': ' . $debug . ' ' . pods_get_debug_timing() );

		return;
	}

	if ( defined( 'WP_CLI' ) ) {
		if ( ! is_string( $debug ) ) {
			$debug = var_export( $debug, true );
		}

		if ( '_null' === $prefix ) {
			$prefix = 'Pods Debug';
		}

		WP_CLI::debug( $prefix . ': ' . $debug );

		return;
	}

	ob_start();

	if ( '_null' !== $prefix ) {
		var_dump( $prefix );
	}

	$debug_line_number = 0;

	if ( '_null' !== $debug ) {
		var_dump( $debug );

		$debug_line_number = __LINE__ - 2;
	} else {
		var_dump( 'Pods Debug #' . $pods_debug . ' ' . pods_get_debug_timing() );

		$debug_line_number = __LINE__ - 2;
	}

	$debug_line_check = sprintf(
		'<small>%s:%s:</small>',
		__FILE__,
		$debug_line_number
	);

	$debug = ob_get_clean();

	if ( false === strpos( $debug, "<pre class='xdebug-var-dump'" ) ) {
		if ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX ) {
			$debug = esc_html( $debug );
		}

		$debug = '<pre>' . $debug . '</pre>';
	} elseif ( false !== strpos( $debug, $debug_line_check ) ) {
		// Attempt to replace the backtrace file/line from our var_dump() above with where the pods_debug() itself was called.
		$backtrace = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS, 2 );

		if ( ! empty( $backtrace[0] ) ) {
			$debug_line_replace = sprintf(
				'<small>%s:%s:</small>',
				$backtrace[0]['file'],
				$backtrace[0]['line']
			);

			$debug = str_replace( $debug_line_check, $debug_line_replace, $debug );
		}
	}

	$debug = '<e>' . $debug;

	if ( 2 === $die ) {
		wp_die( $debug, '', array( 'back_link' => true ) );
	} elseif ( true === $die ) {
		die( $debug );
	}

	echo $debug;
}

/**
 * Log debug information if WP_DEBUG_LOG is used.
 *
 * @since 2.9.12
 *
 * @param mixed $debug The error message to be thrown / displayed.
 */
function pods_debug_log( $debug ) {
	if ( function_exists( 'codecept_debug' ) || defined( 'WP_CLI' ) ) {
		pods_debug( $debug, false, 'Output from pods_debug_log()' );

		return;
	}

	if ( pods_is_debug_logging_enabled() ) {
		$debug_backtrace = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS, 2 );

		// 0 = This function
		// 1 = The place this function was called.
		$function = $debug_backtrace[1]['function'] ?? '';

		if ( ! empty( $debug_backtrace[1]['class'] ) ) {
			$function = $debug_backtrace[1]['class'] . '::' . $function;
		}

		// Debug backtrace will set the line for the call on the next trace up the chain.
		$line = $debug_backtrace[0]['line']; // Yes, this should be a 0 instead of a 1 here.

		pods_debug_log_data( $debug, 'general', $function, $line );
	}

	if ( in_array( strtolower( (string) WP_DEBUG_LOG ), array( 'true', '1' ), true ) ) {
		$log_path = WP_CONTENT_DIR . '/debug.log';
	} elseif ( is_string( WP_DEBUG_LOG ) ) {
		$log_path = WP_DEBUG_LOG;
	} else {
		return;
	}

	ob_start();

	var_dump( $debug );

	$debug_line_number = __LINE__ - 2;

	$debug_line_check = sprintf(
		'<small>%s:%s:</small>',
		__FILE__,
		$debug_line_number
	);

	$debug = ob_get_clean();

	if ( false === strpos( $debug, "<pre class='xdebug-var-dump'" ) ) {
		if ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX ) {
			$debug = esc_html( $debug );
		}
	} elseif ( false !== strpos( $debug, $debug_line_check ) ) {
		// Attempt to replace the backtrace file/line from our var_dump() above with where the pods_debug() itself was called.
		$backtrace = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS, 2 );

		if ( ! empty( $backtrace[0] ) ) {
			$debug_line_replace = sprintf(
				'<small>%s:%s:</small>',
				$backtrace[0]['file'],
				$backtrace[0]['line']
			);

			$debug = str_replace( $debug_line_check, $debug_line_replace, $debug );
		}
	}

	$debug = 'pods_debug_log: ' . $debug;

	// Log the debug line.
	error_log( $debug, 0, $log_path );
}

/**
 * Check if debug is enabled and should be displayed.
 *
 * @return bool
 * @since  2.7.13
 */
function pods_is_debug_display() {
	return ( WP_DEBUG && WP_DEBUG_DISPLAY );
}

/**
 * Determine if user has admin access
 *
 * @since 2.3.5
 *
 * @param string|array $capabilities Additional capabilities to check
 *
 * @return bool Whether user has admin access
 */
function pods_is_admin( $capabilities = null ) {
	// Normalize the capability we are checking.
	if ( empty( $capabilities ) ) {
		$capabilities = [];
	} else {
		$capabilities = (array) $capabilities;
	}

	if ( is_user_logged_in() ) {
		// Check if on multisite and this is a super admin.
		if ( is_multisite() && is_super_admin() ) {
			return apply_filters( 'pods_is_admin', true, $capabilities, '_super_admin' );
		}

		// Get all the capabilities including Pods Admin capabilities.
		$capabilities = pods_get_admin_capabilities( $capabilities );

		// Check if the user has access to any of the capabilities.
		foreach ( $capabilities as $capability ) {
			if ( current_user_can( $capability ) ) {
				return apply_filters( 'pods_is_admin', true, $capabilities, $capability );
			}
		}
	}

	return apply_filters( 'pods_is_admin', false, $capabilities, null );
}

/**
 * Determine if a specific user has admin access.
 *
 * @since 3.0.0
 *
 * @param string|array $capabilities Additional capabilities to check.
 *
 * @return bool Whether user has admin access.
 */
function pods_is_user_admin( int $user_id, $capabilities = null ): bool {
	// Invalid user.
	if ( $user_id < 1 ) {
		return false;
	}

	// Normalize the capability we are checking.
	if ( empty( $capabilities ) ) {
		$capabilities = [];
	} else {
		$capabilities = (array) $capabilities;
	}

	// Check if on multisite and this is a super admin.
	if ( is_multisite() && is_super_admin( $user_id ) ) {
		return apply_filters( 'pods_is_user_admin', true, $user_id, $capabilities, '_super_admin' );
	}

	// Get all the capabilities including Pods Admin capabilities.
	$capabilities = pods_get_admin_capabilities( $capabilities );

	// Check if the user exists.
	$user = get_userdata( $user_id );

	if ( ! $user instanceof WP_User ) {
		return false;
	}

	// Check if the user has access to any of the capabilities.
	foreach ( $capabilities as $capability ) {
		if ( user_can( $user, $capability ) ) {
			return apply_filters( 'pods_is_user_admin', true, $user_id, $capabilities, $capability );
		}
	}

	return apply_filters( 'pods_is_user_admin', false, $user_id, $capabilities, null );
}

/**
 * Get the list of Pods Admin capabilities.
 *
 * @since 3.0.0
 *
 * @param string|array $capabilities Additional capabilities to merge and include.
 *
 * @return array The list of Pods Admin capabilities.
 */
function pods_get_admin_capabilities( array $other_capabilities = [] ): array {
	$pods_admin_capabilities = [];

	if ( ! is_multisite() ) {
		// Default is_super_admin() checks against this capability.
		$pods_admin_capabilities[] = 'delete_users';
	}

	$pods_admin_capabilities = (array) apply_filters( 'pods_admin_capabilities', $pods_admin_capabilities, $other_capabilities );

	return array_unique( array_filter( array_merge( $pods_admin_capabilities, $other_capabilities ) ) );
}

/**
 * Determine if Developer Mode is enabled
 *
 * @return bool Whether Developer Mode is enabled
 *
 * @since 2.3.0
 */
function pods_developer() {
	if ( defined( 'PODS_DEVELOPER' ) && PODS_DEVELOPER ) {
		return true;
	}

	return false;
}

/**
 * Determine if Tableless Mode is enabled
 *
 * @return bool Whether Tableless Mode is enabled
 *
 * @since 2.3.0
 */
function pods_tableless() {
	if ( defined( 'PODS_TABLELESS' ) && PODS_TABLELESS ) {
		return true;
	}

	return false;
}

/**
 * Determine whether the wp_podsrel table is enabled.
 *
 * @since 2.8.0
 *
 * @param null|Field  $field   The field object.
 * @param null|string $context The context of the podsrel check (lookup/save).
 *
 * @return bool Whether the wp_podsrel table is enabled.
 */
function pods_podsrel_enabled( $field = null, $context = null ) {
	// Disabled when Pods Tableless mode is on.
	if ( pods_tableless() ) {
		return false;
	}

	/**
	 * Allow filtering of whether the wp_podsrel table is enabled.
	 *
	 * @since 2.8.0
	 *
	 * @param bool        $enabled Whether the wp_podsrel table is enabled.
	 * @param null|Field  $field   The field object.
	 * @param null|string $context The context of the podsrel check (lookup/save).
	 */
	return (bool) apply_filters( 'pods_podsrel_enabled', true, $field, $context );
}

/**
 * Determine whether relationship meta storage is enabled.
 *
 * @since 2.8.0
 *
 * @param null|array|Field $field The field object.
 * @param null|array|Pod   $pod   The pod object.
 *
 * @return bool Whether relationship meta storage is enabled.
 */
function pods_relationship_meta_storage_enabled( $field = null, $pod = null ) {
	/**
	 * Allow filtering of whether relationship meta storage is enabled.
	 *
	 * @since 2.8.0
	 *
	 * @param bool             $enabled Whether relationship meta storage table is enabled.
	 * @param null|array|Field $field   The field object.
	 * @param null|array|Pod   $pod     The pod object.
	 */
	return (bool) apply_filters( 'pods_relationship_meta_storage_enabled', true, $field, $pod );
}

/**
 * Determine whether relationship meta storage is enabled for simple relationships.
 *
 * @since 2.8.9
 *
 * @param null|array|Field $field The field object.
 * @param null|array|Pod   $pod   The pod object.
 *
 * @return bool Whether relationship meta storage is enabled.
 */
function pods_relationship_meta_storage_enabled_for_simple_relationships( $field = null, $pod = null ) {
	$enabled = null === $pod || 'meta' === $pod['storage'] || 'settings' === $pod['storage'];

	/**
	 * Allow filtering of whether relationship meta storage is enabled for simple relationships.
	 *
	 * @since 2.8.9
	 *
	 * @param bool             $enabled Whether relationship meta storage table is enabled for simple relationships.
	 * @param null|array|Field $field   The field object.
	 * @param null|array|Pod   $pod     The pod object.
	 */
	return (bool) apply_filters( 'pods_relationship_meta_storage_enabled_for_simple_relationships', $enabled, $field, $pod );
}

/**
 * Determine whether relationship table storage is enabled for simple relationships.
 *
 * @since 2.8.9
 *
 * @param null|array|Field $field The field object.
 * @param null|array|Pod   $pod   The pod object.
 *
 * @return bool Whether relationship table storage is enabled.
 */
function pods_relationship_table_storage_enabled_for_simple_relationships( $field = null, $pod = null ) {
	$enabled = null === $pod || 'table' === $pod['storage'];

	/**
	 * Allow filtering of whether relationship table storage is enabled for simple relationships.
	 *
	 * @since 2.8.9
	 *
	 * @param bool             $enabled Whether relationship table storage table is enabled for simple relationships.
	 * @param null|array|Field $field   The field object.
	 * @param null|array|Pod   $pod     The pod object.
	 */
	return (bool) apply_filters( 'pods_relationship_table_storage_enabled_for_simple_relationships', $enabled, $field, $pod );
}

/**
 * Determine whether relationship table storage is enabled for object based relationships.
 *
 * @since 2.8.16
 *
 * @param null|array|Field $field The field object.
 * @param null|array|Pod   $pod   The pod object.
 *
 * @return bool Whether relationship table storage is enabled.
 */
function pods_relationship_table_storage_enabled_for_object_relationships( $field = null, $pod = null ) {
	/**
	 * Allow filtering of whether relationship table storage is enabled for object based relationships.
	 *
	 * @since 2.8.16
	 *
	 * @param bool             $enabled Whether relationship table storage table is enabled for object based relationships.
	 * @param null|array|Field $field   The field object.
	 * @param null|array|Pod   $pod     The pod object.
	 */
	return (bool) apply_filters( 'pods_relationship_table_storage_enabled_for_object_relationships', false, $field, $pod );
}

/**
 * Determine if Light Mode is enabled
 *
 * @return bool Whether Light Mode is enabled
 *
 * @since 2.7.13
 */
function pods_light() {
	if ( defined( 'PODS_LIGHT' ) && PODS_LIGHT ) {
		return true;
	}

	return false;
}

/**
 * Determine whether Pods is in a demo from pods.io or not.
 *
 * @return bool Whether Pods is in a demo.
 *
 * @since 2.9.12
 */
function pods_is_demo() {
	return (
		1 === (int) pods_v( 'pods_wasm_demo' )
		|| 1 === (int) pods_v( 'pods_demo' )
	);
}

/**
 * Determine if Strict Mode is enabled
 *
 * @param bool $include_debug Whether to include WP_DEBUG in strictness level
 *
 * @return bool Whether Strict Mode is enabled
 *
 * @since 2.3.5
 */
function pods_strict( $include_debug = true ) {
	$strict = false;

	if ( defined( 'PODS_STRICT' ) && PODS_STRICT ) {
		// Deprecated PODS_STRICT_MODE since 2.3.5
		$strict = true;
	} elseif ( defined( 'PODS_STRICT_MODE' ) && PODS_STRICT_MODE && pods_allow_deprecated( false ) ) {
		$strict = true;
	} elseif ( $include_debug && defined( 'WP_DEBUG' ) && WP_DEBUG ) {
		$strict = true;
	}

	/**
	 * Allow filtering of whether strict mode is enabled.
	 *
	 * @param boolean $strict Whether strict mode is enabled.
	 *
	 * @since 2.8.0
	 */
	return apply_filters( 'pods_strict_mode', $strict );
}

/**
 * Determine if Deprecated Mode is enabled
 *
 * @param bool $include_debug Whether to include strict mode
 *
 * @return bool Whether Deprecated Mode is enabled
 *
 * @since 2.3.10
 */
function pods_allow_deprecated( $strict = true ) {
	if ( $strict && pods_strict( false ) ) {
		return false;
	} elseif ( ! defined( 'PODS_DEPRECATED' ) || PODS_DEPRECATED ) {
		return true;
	}

	return false;
}

/**
 * Determine if Pods API Caching is enabled
 *
 * @return bool Whether Pods API Caching is enabled
 *
 * @since 2.3.9
 */
function pods_api_cache() {
	if ( defined( 'PODS_API_CACHE' ) && ! PODS_API_CACHE ) {
		return false;
	}

	/**
	 * Filter whether to use the Pods API cache.
	 *
	 * @param boolean $use_cache Whether to use the Pods API cache.
	 *
	 * @since 2.8.0
	 */
	return apply_filters( 'pods_api_cache', true );
}

/**
 * Determine if Pods shortcodes can evaluate magic tags.
 *
 * @since 2.7.16
 *
 * @return bool
 */
function pods_shortcode_allow_evaluate_tags() {
	if ( defined( 'PODS_SHORTCODE_ALLOW_EVALUATE_TAGS' ) && PODS_SHORTCODE_ALLOW_EVALUATE_TAGS ) {
		return true;
	}

	return false;
}

/**
 * Marks a function as deprecated and informs when it has been used.
 *
 * There is a hook deprecated_function_run that will be called that can be used
 * to get the backtrace up to what file and function called the deprecated
 * function.
 *
 * The current behavior is to trigger a user error if WP_DEBUG is true.
 *
 * This function is to be used in every function that is deprecated.
 *
 * @uses  do_action() Calls 'deprecated_function_run' and passes the function name, what to use instead,
 *   and the version the function was deprecated in.
 * @uses  apply_filters() Calls 'deprecated_function_trigger_error' and expects boolean value of true to do
 *   trigger or false to not trigger error.
 *
 * @param string $function    The function that was called
 * @param string $version     The version of WordPress that deprecated the function
 * @param string $replacement Optional. The function that should have been called
 *
 * @since 2.0.0
 */
function pods_deprecated( $function, $version, $replacement = null ) {
	if ( ! version_compare( $version, PODS_VERSION, '<=' ) && ! version_compare( $version . '-a-0', PODS_VERSION, '<=' ) ) {
		return;
	}

	do_action( 'deprecated_function_run', $function, $replacement, $version );

	// Allow plugin to filter the output error trigger
	if ( WP_DEBUG && apply_filters( 'deprecated_function_trigger_error', true ) ) {
		if ( ! is_null( $replacement ) ) {
			$error = __( '%1$s has been <strong>deprecated</strong> since Pods version %2$s! Use %3$s instead.', 'pods' );
		} else {
			$error = __( '%1$s has been <strong>deprecated</strong> since Pods version %2$s with no alternative available.', 'pods' );
		}

		trigger_error( sprintf( $error, $function, $version, $replacement ), E_USER_DEPRECATED );
	}
}

/**
 * Inline help
 *
 * @param string      $text      Help text.
 * @param null|string $url       Documentation URL.
 * @param null|string $container The HTML container path for where the inline help will live.
 *
 * @return null|string The help output or null if echoing.
 *
 * @since 2.0.0
 */
function pods_help( $text, $url = null, $container = null, $return = false ) {
	if ( ! wp_script_is( 'jquery-qtip2', 'registered' ) ) {
		wp_register_script( 'jquery-qtip2', PODS_URL . 'ui/js/qtip/jquery.qtip.min.js', array( 'jquery' ), '3.0.3' );
		wp_enqueue_script( 'jquery-qtip2' );
	} elseif ( ! wp_script_is( 'jquery-qtip2', 'queue' ) && ! wp_script_is( 'jquery-qtip2', 'to_do' ) && ! wp_script_is( 'jquery-qtip2', 'done' ) ) {
		wp_enqueue_script( 'jquery-qtip2' );
	}

	if ( ! wp_script_is( 'pods-qtip-init', 'registered' ) ) {
		wp_register_script( 'pods-qtip-init', PODS_URL . 'ui/js/qtip.js', array(
			'jquery',
			'jquery-qtip2',
		), PODS_VERSION );
		pods_form_enqueue_script( 'pods-qtip-init' );
	} elseif ( ! wp_script_is( 'pods-qtip-init', 'queue' ) && ! wp_script_is( 'pods-qtip-init', 'to_do' ) && ! wp_script_is( 'pods-qtip-init', 'done' ) ) {
		pods_form_enqueue_script( 'pods-qtip-init' );
	}

	pods_form_enqueue_script( 'pods' );
	pods_form_enqueue_style( 'pods' );

	static $pods_qtip_added = [];

	if ( null !== $container && ! isset( $pods_qtip_added[ $container ] ) ) {
		wp_add_inline_script(
			'pods',
			"
				jQuery( function ( $ ) {
					$( document ).Pods( 'qtip', '" . esc_js( $container ) . "' );
				} );
			"
		);

		$pods_qtip_added[ $container ] = true;
	}

	if ( is_array( $text ) ) {
		if ( isset( $text[1] ) ) {
			$url = $text[1];
		}

		$text = $text[0];
	}

	if ( 'help' === $text ) {
		if ( $return ) {
			return '';
		}

		return null;
	}

	if ( $url && 0 < strlen( $url ) ) {
		$text .= '<br /><br /><a href="' . esc_url( $url ) . '" target="_blank" rel="noopener noreferrer">' . esc_html__( 'Find out more', 'pods' ) . ' &raquo;</a>';
	}

	$text = wpautop( $text );

	$output = '<img src="' . esc_url( PODS_URL ) . 'ui/images/help.png" alt="' . esc_attr( $text ) . '" class="pods-icon pods-qtip" />';

	if ( $return ) {
		return $output;
	}

	echo $output;

	return null;
}

/**
 * Check whether something is a specific version minimum and/or maximum
 *
 * @param string $minimum_version Minimum version
 * @param string $comparison      Comparison operator
 * @param string $maximum_version Maximum version
 *
 * @return bool
 */
function pods_version_check( $what, $minimum_version, $comparison = '<=', $maximum_version = null ) {
	global $wp_version, $wpdb;

	if ( 'php' === $what ) {
		$version = phpversion();
	} elseif ( 'mysql' === $what ) {
		$version = $wpdb->db_version();
	} else {
		$version = $wp_version;
	}

	if ( ! empty( $minimum_version ) && ! version_compare( $minimum_version, $version, $comparison ) ) {
		return false;
	}

	if ( ! empty( $maximum_version ) && ! version_compare( $version, $maximum_version, $comparison ) ) {
		return false;
	}

	return true;
}

/**
 * Run a Pods Helper
 *
 * @param string $helper_name Helper Name
 * @param string $value       Value to run Helper on
 * @param string $name        Field name.
 *
 * @return bool
 * @since 1.7.5
 */
function pods_helper( $helper_name, $value = null, $name = null ) {
	return pods()->helper( $helper_name, $value, $name );
}

/**
 * Get the current hostname.
 *
 * @since 2.8.0
 *
 * @return string The current hostname.
 */
function pods_current_host() {
	if ( empty( $_SERVER['HTTP_HOST'] ) ) {
		$host = wp_parse_url( get_site_url(), PHP_URL_HOST );

		if ( empty( $host ) ) {
			return 'localhost';
		}

		return $host;
	}

	return $_SERVER['HTTP_HOST'];
}

/**
 * Get the full path of the current page.
 *
 * @since 2.8.0
 *
 * @return string Full path of the current page.
 */
function pods_current_path() {
	if ( empty( $_SERVER['REQUEST_URI'] ) ) {
		return '/';
	}

	return $_SERVER['REQUEST_URI'];
}

/**
 * Get the full URL of the current page.
 *
 * @since 2.3.0
 *
 * @return string Full URL of the current page.
 */
function pods_current_url() {
	$url = 'http';

	if ( isset( $_SERVER['HTTPS'] ) && 'off' !== $_SERVER['HTTPS'] && 0 !== $_SERVER['HTTPS'] ) {
		$url = 'https';
	}

	$url .= '://' . pods_current_host() . pods_current_path();

	return apply_filters( 'pods_current_url', $url );
}

/**
 * Find out if the current page has a valid $pods
 *
 * @param object $object The Pod Object currently checking (optional)
 *
 * @return bool
 * @since 2.0.0
 */
function is_pod( $object = null ) {
	global $pods, $post;

	if ( is_object( $object ) && isset( $object->pod ) && ! empty( $object->pod ) ) {
		return true;
	} elseif ( is_object( $pods ) && isset( $pods->pod ) && ! empty( $pods->pod ) ) {
		return true;
	} elseif ( is_object( $post ) && isset( $post->post_type ) && pods_api()->pod_exists( $post->post_type, 'post_type' ) ) {
		return true;
	}

	return false;
}

/**
 * See if the current user has a certain privilege
 *
 * @param mixed  $privs  The privilege name or names (array if multiple)
 * @param string $method The access method ("AND", "OR")
 *
 * @return bool
 * @since 1.2.0
 */
function pods_access( $privs, $method = 'OR' ) {
	// Convert $privs to an array
	$privs = (array) $privs;

	// Convert $method to uppercase
	$method = strtoupper( $method );

	$check = apply_filters( 'pods_access', null, $privs, $method );
	if ( null !== $check && is_bool( $check ) ) {
		return $check;
	}

	if ( ! is_user_logged_in() ) {
		return false;
	}

	if ( pods_is_admin( array( 'pods', 'pods_content' ) ) ) {
		return true;
	}

	// Store approved privs when using "AND"
	$approved_privs = array();

	// Loop through the user's roles
	foreach ( $privs as $priv ) {
		if ( 0 === strpos( $priv, 'pod_' ) ) {
			$priv = pods_str_replace( 'pod_', 'pods_edit_', $priv, 1 );
		}

		if ( 0 === strpos( $priv, 'manage_' ) ) {
			$priv = pods_str_replace( 'manage_', 'pods_', $priv, 1 );
		}

		if ( current_user_can( $priv ) ) {
			if ( 'OR' === $method ) {
				return true;
			}

			$approved_privs[ $priv ] = true;
		}
	}
	if ( 'AND' === strtoupper( $method ) ) {
		foreach ( $privs as $priv ) {
			if ( 0 === strpos( $priv, 'pod_' ) ) {
				$priv = pods_str_replace( 'pod_', 'pods_edit_', $priv, 1 );
			}

			if ( 0 === strpos( $priv, 'manage_' ) ) {
				$priv = pods_str_replace( 'manage_', 'pods_', $priv, 1 );
			}

			if ( ! isset( $approved_privs[ $priv ] ) ) {
				return false;
			}
		}

		return true;
	}

	return false;
}

/**
 * Check whether a Pods shortcode is currently being parsed.
 * If a boolean is passed it overwrites the status.
 *
 * @param bool $bool
 *
 * @return bool
 * @since  2.7.13
 */
function pods_doing_shortcode( $bool = null ) {
	static $check = false;
	if ( null !== $bool ) {
		$check = (bool) $bool;
	}
	return $check;
}

/**
 * Check whether we are currently in a JSON request.
 *
 * @since  2.8.0
 *
 * @return bool Whether we are in a REST API or JSON request.
 */
function pods_doing_json() {
	// Check whether we are doing a REST API request.
	if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
		return true;
	}

	// Return whether we are doing a JSON request.
	return wp_is_json_request();
}

/**
 * Shortcode support for use anywhere that support WP Shortcodes.
 * Will return error message on failure.
 *
 * @since 1.6.7
 * @since 2.7.13 Try/Catch.
 *
 * @param array  $tags    An associative array of shortcode properties.
 * @param string $content A string that represents a template override.
 *
 * @return string
 */
function pods_shortcode( $tags, $content = null ) {
	return pods_shortcode_run_safely( $tags, $content );
}

/**
 * Shortcode support for use anywhere that support WP Shortcodes.
 * Will return error message on failure.
 *
 * @since 3.1.0
 *
 * @param array       $tags                        An associative array of shortcode properties.
 * @param string|null $content                     A string that represents a template override.
 * @param bool        $check_display_access_rights Whether to check access rights for the embedded content.
 *
 * @return string
 */
function pods_shortcode_run_safely( $tags, ?string $content = null, bool $check_display_access_rights = true ): string {
	pods_doing_shortcode( true );

	$return_exception = static function() {
		return 'exception';
	};

	add_filter( 'pods_error_mode', $return_exception, 50 );
	add_filter( 'pods_error_exception_fallback_enabled', '__return_false', 50 );

	$blog_is_switched = false;

	if ( defined( 'PODS_SHORTCODE_ALLOW_BLOG_SWITCHING' ) && PODS_SHORTCODE_ALLOW_BLOG_SWITCHING && is_multisite() ) {
		if ( ! empty( $tags['blog_id'] ) && is_numeric( $tags['blog_id'] ) && (int) get_current_blog_id() !== (int) $tags['blog_id'] ) {
			switch_to_blog( (int) $tags['blog_id'] );

			$blog_is_switched = true;
		}
	}

	try {
		$return = pods_shortcode_run( $tags, $content, $blog_is_switched, $check_display_access_rights );
	} catch ( Throwable $throwable ) {
		/**
		 * Allow filtering whether to throw errors for the shortcode.
		 *
		 * @since 3.0.9
		 *
		 * @param bool $throw_errors Whether to throw errors for the shortcode.
		 */
		$throw_errors = apply_filters( 'pods_shortcode_throw_errors', false );

		if ( $throw_errors ) {
			if ( $blog_is_switched ) {
				restore_current_blog();
			}

			throw $throwable;
		}

		$return = '';

		if ( pods_is_debug_display() ) {
			$return = pods_message(
				sprintf(
					'<strong>%1$s:</strong> %2$s',
					esc_html__( 'Pods Renderer Error', 'pods' ),
					esc_html( $throwable->getMessage() )
				),
				'error',
				true
			);

			$return .= '<pre style="overflow:scroll">' . esc_html( $throwable->getTraceAsString() ) . '</pre>';
		} elseif (
			is_user_logged_in()
			&& (
				is_admin()
				|| (
					wp_is_json_request()
					&& did_action( 'rest_api_init' )
				)
			)
		) {
			$return = pods_message(
				sprintf(
					'<strong>%1$s:</strong> %2$s',
					esc_html__( 'Pods Renderer Error', 'pods' ),
					esc_html__( 'There was a problem displaying this content, enable WP_DEBUG in wp-config.php to show more details.', 'pods' )
				),
				'error',
				true
			);
		}
	}

	if ( $blog_is_switched ) {
		restore_current_blog();
	}

	remove_filter( 'pods_error_mode', $return_exception, 50 );
	remove_filter( 'pods_error_exception_fallback_enabled', '__return_false', 50 );

	pods_doing_shortcode( false );

	return $return;
}

/**
 * Wrap the HTML using attributes for the element.
 *
 * This is used to support Blocks and other elements that use custom attributes like class and anchor.
 *
 * @since 2.8.9
 *
 * @param string $html       The HTML to wrap.
 * @param array  $attributes List of attributes for the element.
 *
 * @return string The wrapped HTML.
 */
function pods_wrap_html( $html, $attributes = [] ) {
	if ( empty( $attributes ) || '' === trim( $html ) ) {
		return $html;
	}

	$container = [];

	// Handle support for className.
	if ( ! empty( $attributes['className'] ) ) {
		$container['class'] = trim( $attributes['className'] );
	}

	// Handle align support.
	if ( ! empty( $attributes['align'] ) ) {
		if ( empty( $container['class'] ) ) {
			$container['class'] = '';
		}

        $container['class'] = trim( 'align' . $attributes['align'] . ' ' . $container['class'] );
    }

	// Handle support for anchor.
	if ( ! empty( $attributes['anchor'] ) ) {
		$container['id'] = $attributes['anchor'];
	}

	if ( empty( $container ) ) {
		return $html;
	}

	ob_start();
	PodsForm::attributes( $container, '_pods_wrap', '_pods_wrap', $attributes );
	$html_attributes = ob_get_clean();

	return sprintf( '<div%1$s>%2$s</div>', $html_attributes, $html );
}

/**
 * Detect the shortcode context from the shortcode attributes.
 *
 * @since 3.2.7
 *
 * @param array $attributes The list of shortcode attributes.
 *
 * @return string|null The context or null if no context detected.
 */
function pods_shortcode_detect_context( array $attributes ) : ?string {
	$context = $attributes['context'] ?? null;

	if ( $context ) {
		return $context;
	}

	if ( $attributes['view'] ?? null ) {
		$context = 'view';
	} elseif ( $attributes['form'] ?? null ) {
		$context = 'form';
	} elseif ( $attributes['related_field'] ?? null ) {
		$context = 'related-item-list';
	} else {
		$template_custom = $attributes['template_custom'] ?? '';
		$field           = $attributes['field'] ?? $attributes['col'] ?? '';
		$slug            = $attributes['slug'] ?? '';
		$id              = $attributes['id'] ?? '';

		if ( false !== strpos( $template_custom, '{@_all_fields' ) ) {
			$context = 'item-single-list-fields';
		} elseif ( '' !== $field ) {
			$context = 'field';
		} elseif ( ! empty( $slug ) || ! empty( $id ) ) {
			$context = 'item-single';
		} else {
			$context = 'item-list';
		}
	}

	return $context;
}

/**
 * Get the query var affix to use in shortcodes.
 *
 * @since 3.2.7
 *
 * @param array $attributes The list of shortcode attributes.
 *
 * @return string The query var affix to use in shortcodes.
 */
function pods_shortcode_query_var_affix( array $attributes ): string {
	$affix_counter = (int) pods_static_cache_get( 'affix_counter', __FUNCTION__ );

	if ( 0 === $affix_counter ) {
		$affix_counter = 1;
	}

	$query_var_affix = $attributes['query_var_affix'];

	if ( null === $query_var_affix ) {
		$query_var_affix = 1 < $affix_counter ? '_' . $affix_counter : '';

		$affix_counter ++;

		pods_static_cache_set( 'affix_counter', $affix_counter, __FUNCTION__ );
	}

	return $query_var_affix;
}

/**
 * Shortcode support for use anywhere that support WP Shortcodes.
 *
 * @since 2.7.13
 *
 * @param array       $tags                        An associative array of shortcode properties.
 * @param string|null $content                     A string that represents a template override.
 * @param bool        $blog_is_switched            Whether the blog is switched.
 * @param bool        $check_display_access_rights Whether to check access rights for the embedded content.
 *
 * @return string
 */
function pods_shortcode_run( $tags, $content = null, $blog_is_switched = false, $check_display_access_rights = true ) {
	if ( defined( 'PODS_DISABLE_SHORTCODE' ) && PODS_DISABLE_SHORTCODE ) {
		if ( empty( $tags['field'] ) && pods_is_admin() ) {
			return pods_get_access_admin_notice( [
				'content' => esc_html__( 'Pods dynamic features are disabled.', 'pods' ),
			] );
		}

		return '';
	}

	$context = pods_shortcode_detect_context( $tags );

	$supported_contexts = [
		'field',
		'form',
		'item-list',
		'item-single',
		'item-single-list-fields',
		'related-item-list',
		'view',
	];

	if ( ! in_array( $context, $supported_contexts, true ) ) {
		return pods_message(
			sprintf(
				'<strong>%1$s:</strong> %2$s',
				esc_html__( 'Pods Embed Error', 'pods' ),
				esc_html( sprintf( __( 'Unsupported context: %s.', 'pods' ), $context ?? 'unknown' ) )
			),
			'error',
			true
		);
	}

	// For enforcing pagination parameters when not displaying pagination
	$page   = 1;
	$offset = 0;

	if ( isset( $tags['page'] ) ) {
		$page = (int) $tags['page'];
		$page = max( $page, 1 );
	}

	if ( isset( $tags['offset'] ) ) {
		$offset = (int) $tags['offset'];
		$offset = max( $offset, 0 );
	}

	// Query related tags separated to use later.
	$default_query_tags = array(
		'use_current'         => false,
		'bypass_detect_loop'  => false,
		'name'                => null,
		'id'                  => null,
		'slug'                => null,
		'select'              => null,
		'join'                => null,
		'order'               => null, // deprecated
		'orderby'             => null,
		'limit'               => null,
		'where'               => null,
		'having'              => null,
		'groupby'             => null,
		'search'              => true,
		'pagination'          => false,
		'page'                => null,
		'offset'              => null,
		'filters_enable'      => null,
		'filters'             => '',
		'filters_label'       => null,
		'filters_location'    => 'before',
		'pagination_label'    => null,
		'pagination_type'     => null,
		'pagination_location' => 'after',
		'search_var'          => null,
		'page_var'            => null,
		'filter_var'          => null,
		'query_var_affix'     => null,
	);

	$default_other_tags = [
		'blog_id'          => null,
		'field'            => null,
		'related_field'    => null,
		'link_field'       => null,
		'col'              => null, // deprecated
		'template'         => null,
		'template_custom'  => $content,
		'pods_page'        => null,
		'helper'           => null,
		'form'             => null,
		'form_output_type' => 'div',
		'form_key'         => null,
		'fields'           => null,
		'label'            => null,
		'thank_you'        => null,
		'not_found'        => null,
		'view'             => null,
		'cache_mode'       => 'none',
		'expires'          => 0,
		'shortcodes'       => false,
		'className'        => null,
		'anchor'           => null,
		'align'            => null,
	];

	$defaults = array_merge( $default_other_tags, $default_query_tags );

	$original_tags = $tags;

	if ( ! empty( $tags ) ) {
		$tags = array_merge( $defaults, $tags );
	} else {
		$tags = $defaults;
	}

	if ( ! empty( $tags['template_custom'] ) ) {
		$content = $tags['template_custom'];
	}

	// Bypass custom select if it might be aliasing or selecting data we don't want to work with.
	if (
		! empty( $tags['select'] )
		&& is_string( $tags['select'] )
		&& (
			false !== stripos( $tags['select'], 'user_pass' )
			|| false !== stripos( $tags['select'], 'user_activation_key' )
			|| false !== stripos( $tags['select'], 'post_password' )
		)
	) {
		$tags['select'] = null;
	}

	$tags = apply_filters( 'pods_shortcode', $tags, $content, $original_tags );

	$tags['pagination']  = filter_var( $tags['pagination'], FILTER_VALIDATE_BOOLEAN );
	$tags['search']      = filter_var( $tags['search'], FILTER_VALIDATE_BOOLEAN );
	$tags['use_current'] = filter_var( $tags['use_current'], FILTER_VALIDATE_BOOLEAN );

	pods_debug_log_data( pods_array_filter_null_and_empty_string( $tags ), 'shortcode-args', __METHOD__, __LINE__ );

	if ( empty( $content ) ) {
		$content = null;
	}

	// Allow views only if not targeting a file path (must be within theme)
	if ( 'view' === $context ) {
		$return = '';

		// Confirm the feature is enabled and the file is allowed.
		if (
			pods_can_use_dynamic_feature( 'view' )
			&& PodsView::view_get_path( $tags['view'], true )
		) {
			$return = pods_view( $tags['view'], null, (int) $tags['expires'], $tags['cache_mode'], true, true );

			if ( $tags['shortcodes'] && defined( 'PODS_SHORTCODE_ALLOW_SUB_SHORTCODES' ) && PODS_SHORTCODE_ALLOW_SUB_SHORTCODES ) {
				$return = do_shortcode( $return );
			}

			$return = pods_wrap_html( $return, $tags );
		}

		pods_debug_log_data( pods_array_filter_null_and_empty_string( $tags ), 'shortcode-final-args', __METHOD__, __LINE__ );

		/**
		 * Allow customization of shortcode output based on shortcode attributes.
		 *
		 * @since 2.7.9
		 *
		 * @param string    $return  Shortcode output to return.
		 * @param array     $tags    Shortcode attributes.
		 * @param null|Pods $pod     Pods object, or null if 'view' context.
		 * @param string    $context The shortcode context (form, field, pods-page, view, or list).
		 */
		return apply_filters( 'pods_shortcode_output', $return, $tags, null, 'view' );
	}

	$is_form = 'form' === $context;

	// If the feature is disabled then return early.
	if ( $is_form && ! pods_can_use_dynamic_feature( 'form' ) ) {
		if ( pods_is_admin() ) {
			return pods_get_access_admin_notice( [
				'content' => esc_html__( 'The Pods Form dynamic feature is disabled and this embed will not show.', 'pods' ),
			] );
		}

		return '';
	} elseif ( ! $is_form && ! pods_can_use_dynamic_feature( 'display' ) ) {
		if ( empty( $tags['field'] ) && pods_is_admin() ) {
			return pods_get_access_admin_notice( [
				'content' => esc_html__( 'The Pods Display dynamic feature is disabled and this embed will not show.', 'pods' ),
			] );
		}

		return '';
	}

	if ( ! $tags['use_current'] && empty( $tags['name'] ) ) {
		$has_query_tags = array_intersect_key( array_diff( $tags, $defaults ), $default_query_tags );

		// Only allow revert to current object if there are no query tags.
		if ( ! $has_query_tags ) {
			/**
			 * Allow filtering whether to detect the pod name / item ID from the current post object.
			 *
			 * @since 2.7.26
			 *
			 * @param bool  $detect_from_current  Whether to detect the pod name / item ID from the current post object.
			 * @param array $shortcode_attributes The list of attributes used for the shortcode.
			 */
			$detect_from_current = apply_filters( 'pods_shortcode_detect_from_current_post', 1 === (int) $tags['bypass_detect_loop'] || in_the_loop(), $tags );

			// Archives, Post type archives, singular posts.
			if ( $detect_from_current ) {
				$pod = pods( get_post_type(), get_the_ID(), false );

				if ( ! empty( $pod ) ) {
					$id           = get_the_ID();
					$tags['id']   = $id;
					$tags['name'] = get_post_type();
				}
			} else {
				$tags['use_current'] = true;
			}
		}

		if ( ! $tags['use_current'] && empty( $tags['name'] ) ) {
			return pods_message(
				sprintf(
					'<strong>%1$s:</strong> %2$s',
					esc_html__( 'Pods Embed Error', 'pods' ),
					esc_html__( 'Please provide a Pod name.', 'pods' )
				),
				'error',
				true
			);
		}
	}

	if ( ! empty( $tags['col'] ) ) {
		$tags['field'] = $tags['col'];

		unset( $tags['col'] );
	}

	if ( ! empty( $tags['order'] ) ) {
		$tags['orderby'] = $tags['order'];

		unset( $tags['order'] );
	}

	if ( empty( $content ) && empty( $tags['pods_page'] ) && empty( $tags['template'] ) && empty( $tags['field'] ) && empty( $tags['form'] ) ) {
		return pods_message(
			sprintf(
				'<strong>%1$s:</strong> %2$s',
				esc_html__( 'Pods Embed Error', 'pods' ),
				esc_html__( 'Please provide either a template or field name.', 'pods' )
			),
			'error',
			true
		);
	}

	if ( ! $tags['use_current'] && ! isset( $id ) ) {
		// id > slug (if both exist)
		$id = null;

		$evaluate_tags_args = array(
			'sanitize'        => true,
			'fallback'        => null,
			'use_current_pod' => true,
		);

		if ( ! empty( $tags['slug'] ) ) {
			$id = $tags['slug'];

			if ( pods_shortcode_allow_evaluate_tags() ) {
				$id = pods_evaluate_tags( $id, $evaluate_tags_args );
			}
		}

		if ( ! empty( $tags['id'] ) ) {
			$id = $tags['id'];

			if ( pods_shortcode_allow_evaluate_tags() ) {
				$id = pods_evaluate_tags( $id, $evaluate_tags_args );
			}

			if ( is_numeric( $id ) ) {
				$id = absint( $id );
			}
		}
	}//end if

	if ( ! isset( $pod ) ) {
		if ( ! $tags['use_current'] ) {
			$pod = pods( $tags['name'], $id );
		} else {
			$pod = pods();
			$id  = $pod->id();
		}
	}

	if ( empty( $pod ) || ! $pod->valid() ) {
		return pods_message(
			sprintf(
				'<strong>%1$s:</strong> %2$s',
				esc_html__( 'Pods Embed Error', 'pods' ),
				esc_html__( 'Pod not found.', 'pods' )
			),
			'error',
			true
		);
	}

	$found = 0;
	$filters = false;

	$is_singular = ( ! empty( $id ) || $tags['use_current'] );

	$return = '';

	$info = pods_info_from_args( [
		'item_id' => $is_singular ? $id : null,
		'pods'    => $pod,
	] );

	// Determine if this is is a public content type.
	if (
		$check_display_access_rights
	) {
		// Check access rights for editor mode and preview of this embed.
		if ( ! empty( $tags['_is_editor_mode'] ) || ! empty( $tags['_is_preview'] ) || is_preview() ) {
			$check_post_id = ! empty( $tags['_preview_id'] ) ? (int) $tags['_preview_id'] : get_queried_object_id();

			if ( ! current_user_can( 'publish_post', $check_post_id ) && ! pods_is_admin() ) {
				// Stop display and only return the notice.
				return empty( $tags['field'] ) ? pods_get_access_user_notice( $info, true, esc_html__( 'You do not have the capability to preview this Pods embed.', 'pods' ) ) : '';
			}
		}

		$access_type = 'read';

		if ( $is_form ) {
			$access_type = $is_singular ? 'edit' : 'add';
		}

		$is_type_public = pods_is_type_public( $info );
		$can_use_dynamic_features_for_pod = pods_can_use_dynamic_features( $info['pod'] );
		$can_use_unrestricted = pods_can_use_dynamic_feature_unrestricted( $info, $is_form ? 'form' : 'display', $access_type );

		if (
			! $can_use_dynamic_features_for_pod
			|| (
				! $is_type_public
				&& ! $can_use_unrestricted
			)
		) {
			if ( ! $is_type_public ) {
				// Stop handling the display and return the access notice if they do not have access to the private content type.
				if ( ! pods_current_user_can_access_object( $info, $access_type, 'shortcode' ) ) {
					// Stop display and only return the notice.
					return empty( $tags['field'] ) ? pods_get_access_user_notice( $info ) : '';
				}

				// Show the admin-specific notice that this content may not be visible to others since it is not public.
				if ( empty( $tags['field'] ) && pods_is_admin() ) {
					// Include the notice in the display output to let the admin know and continue the display.
					$return .= pods_get_access_admin_notice( $info );
				}
			} elseif (
				pods_access_bypass_post_with_password( $info )
				|| pods_access_bypass_private_post( $info )
			) {
				// Stop display and only return the notice.
				return empty( $tags['field'] ) ? pods_get_access_user_notice( $info ) : '';
			}
		}
	}

	$related_field = null;

	$is_related_item_list = 'related-item-list' === $context;

	if ( $is_related_item_list ) {
		$related_field = $pod->pod_data->get_field( $tags['related_field'] );

		if ( ! $related_field ) {
			return pods_message(
				sprintf(
					'<strong>%1$s:</strong> %2$s',
					esc_html__( 'Pods Embed Error', 'pods' ),
					esc_html( sprintf( __( 'Related field not found: %s.', 'pods' ), $tags['related_field'] ) )
				),
				'error',
				true
			);
		}

		$is_singular = false;
	}

	if ( ! $is_singular ) {
		$params = array();

		$can_use_dynamic_feature_all_sql_clauses    = pods_can_use_dynamic_feature_sql_clauses( 'all' );
		$can_use_dynamic_feature_simple_sql_clauses = pods_can_use_dynamic_feature_sql_clauses( 'simple' );
		$shortcode_allow_evaluate_tags              = pods_shortcode_allow_evaluate_tags();

		$evaluate_tags_args = array(
			'sanitize'        => true,
			'fallback'        => '""',
			'use_current_pod' => true,
		);

		if ( $tags['select'] && 0 < strlen( (string) $tags['select'] ) ) {
			if ( ! $can_use_dynamic_feature_simple_sql_clauses || ! pods_access_sql_fragment_is_allowed( $tags['select'], 'SELECT', $info ) ) {
				return pods_message(
					sprintf(
						'<strong>%1$s:</strong> %2$s',
						esc_html__( 'Pods Embed Error', 'pods' ),
						esc_html__( 'SELECT contains SQL that is not allowed.', 'pods' )
					),
					'error',
					true
				);
			}

			$params['select'] = $tags['select'];
		}

		if ( $tags['join'] && 0 < strlen( (string) $tags['join'] ) ) {
			if ( ! $can_use_dynamic_feature_all_sql_clauses || ! pods_access_sql_fragment_is_allowed( $tags['join'], 'JOIN', $info ) ) {
				return pods_message(
					sprintf(
						'<strong>%1$s:</strong> %2$s',
						esc_html__( 'Pods Embed Error', 'pods' ),
						esc_html__( 'JOIN contains SQL that is not allowed.', 'pods' )
					),
					'error',
					true
				);
			}

			$params['join'] = $tags['join'];
		}

		if ( $tags['where'] && 0 < strlen( (string) $tags['where'] ) ) {
			$tags['where'] = ltrim( $tags['where'], ')' );

			if ( ! $can_use_dynamic_feature_simple_sql_clauses || ! pods_access_sql_fragment_is_allowed( $tags['where'], 'WHERE', $info ) ) {
				return pods_message(
					sprintf(
						'<strong>%1$s:</strong> %2$s',
						esc_html__( 'Pods Embed Error', 'pods' ),
						esc_html__( 'WHERE contains SQL that is not allowed.', 'pods' )
					),
					'error',
					true
				);
			}

			$params['where'] = $tags['where'];

			if ( $shortcode_allow_evaluate_tags ) {
				$params['where'] = pods_evaluate_tags_sql( html_entity_decode( $params['where'] ), $evaluate_tags_args );
			}
		}

		if ( $tags['groupby'] && 0 < strlen( (string) $tags['groupby'] ) ) {
			if ( ! $can_use_dynamic_feature_all_sql_clauses || ! pods_access_sql_fragment_is_allowed( $tags['groupby'], 'GROUP BY', $info ) ) {
				return pods_message(
					sprintf(
						'<strong>%1$s:</strong> %2$s',
						esc_html__( 'Pods Embed Error', 'pods' ),
						esc_html__( 'GROUP BY contains SQL that is not allowed.', 'pods' )
					),
					'error',
					true
				);
			}

			$params['groupby'] = $tags['groupby'];
		}

		if ( $tags['having'] && 0 < strlen( (string) $tags['having'] ) ) {
			$tags['having'] = ltrim( $tags['having'], ')' );

			if ( ! $can_use_dynamic_feature_all_sql_clauses || ! pods_access_sql_fragment_is_allowed( $tags['having'], 'HAVING', $info ) ) {
				return pods_message(
					sprintf(
						'<strong>%1$s:</strong> %2$s',
						esc_html__( 'Pods Embed Error', 'pods' ),
						esc_html__( 'HAVING contains SQL that is not allowed.', 'pods' )
					),
					'error',
					true
				);
			}

			$params['having'] = $tags['having'];

			if ( $shortcode_allow_evaluate_tags ) {
				$params['having'] = pods_evaluate_tags_sql( html_entity_decode( $params['having'] ), $evaluate_tags_args );
			}
		}

		if ( $tags['orderby'] && 0 < strlen( (string) $tags['orderby'] ) ) {
			if ( ! $can_use_dynamic_feature_simple_sql_clauses || ! pods_access_sql_fragment_is_allowed( $tags['orderby'], 'ORDER BY', $info ) ) {
				return pods_message(
					sprintf(
						'<strong>%1$s:</strong> %2$s',
						esc_html__( 'Pods Embed Error', 'pods' ),
						esc_html__( 'ORDER BY contains SQL that is not allowed.', 'pods' )
					),
					'error',
					true
				);
			}

			$params['orderby'] = $tags['orderby'];
		}

		// Forms require params set
		if ( ! empty( $params ) || empty( $tags['form'] ) ) {
			if ( ! empty( $tags['limit'] ) ) {
				$params['limit'] = (int) $tags['limit'];
			}

			$params['search']     = $tags['search'];
			$params['pagination'] = $tags['pagination'];

			if ( $params['search'] || $params['pagination'] ) {
				$tags['query_var_affix'] = pods_shortcode_query_var_affix( $tags );

				if ( $params['search'] ) {
					$params['search_var'] = $pod->search_var = $tags['search_var'] ?? ( 'search' . $tags['query_var_affix'] );
					$params['filter_var'] = $pod->filter_var = $tags['filter_var'] ?? ( 'filter' . $tags['query_var_affix'] );

					$params['search_query'] = pods_v( $params['search_var'], 'get', '' );

					if (
						true === (bool) $tags['filters_enable']
						|| (
							! empty( $tags['filters'] )
							&& null === $tags['filters_enable']
						)
					) {
						$params['filters'] = explode( ',', $tags['filters'] );
					}
				}

				if ( $params['pagination'] ) {
					$params['page_var'] = $pod->page_var = $tags['page_var'] ?? ( 'pg' . $tags['query_var_affix'] );

					$params['page'] = max( (int) pods_v( $params['page_var'], 'get', 1 ), 1 );
				}
			}

			// If we aren't displaying pagination, we need to enforce page/offset
			if ( ! $params['pagination'] ) {
				$params['page']   = $page;
				$params['offset'] = $offset;

				// Force pagination on, we need it and we're enforcing page/offset
				$params['pagination'] = true;
			} else {
				// If we are displaying pagination, allow page/offset override only if *set*
				if ( ! empty( $tags['page'] ) ) {
					$params['page'] = (int) $tags['page'];
					$params['page'] = max( $params['page'], 1 );
				}

				if ( ! empty( $tags['offset'] ) ) {
					$params['offset'] = (int) $tags['offset'];
					$params['offset'] = max( $params['offset'], 0 );
				}
			}

			if ( ! empty( $tags['cache_mode'] ) && 'none' !== $tags['cache_mode'] ) {
				$params['cache_mode'] = $tags['cache_mode'];
				$params['expires']    = (int) $tags['expires'];
			}

			$params = apply_filters( 'pods_shortcode_findrecords_params', $params, $pod, $tags );

			if ( $is_related_item_list ) {
				pods_debug_log_data( [ 'field_name' => $tags['related_field'], 'sql' => $params ], 'shortcode-field-params', __METHOD__, __LINE__ );

				// Override the Pods object.
				$pod_check = $pod->field(
					[
						'name'   => $tags['related_field'],
						'output' => 'find',
						'params' => $params,
					]
				);

				if ( ! $pod_check instanceof Pods ) {
					$related_field = $pod->pod_data->get_field( $tags['related_field'] );

					if ( $related_field instanceof Field ) {
						$related_object = $related_field->get_related_object();

						if ( $related_object instanceof Pod ) {
							$pod_check = pods( $related_object->get_name() );
						}
					}
				}

				$pod = $pod_check;

				if ( ! $pod instanceof Pods ) {
					return pods_message(
						sprintf(
							'<strong>%1$s:</strong> %2$s',
							esc_html__( 'Pods Embed Error', 'pods' ),
							esc_html__( 'Related field is not compatible with this block. You may need to extend it with Pods first.', 'pods' )
						),
						'error',
						true
					);
				}
			} else {
				pods_debug_log_data( [ 'field_name' => $tags['related_field'], 'sql' => $params ], 'shortcode-find-params', __METHOD__, __LINE__ );

				$pod->find( $params );
			}

			// Load filters and return HTML for later use.
			if (
				true === (bool) $tags['filters_enable']
				|| (
					! empty( $tags['filters'] )
					&& null === $tags['filters_enable']
				)
			) {
				$filters_params = [
					'fields' => (string) $tags['filters'],
					'label'  => (string) $tags['filters_label'],
				];

				$filters = $pod->filters( $filters_params );
			}

			$found = $pod->total_found();
		}//end if
	}//end if

	// Handle form output.
	if ( $is_form ) {
		if ( 'user' === $pod->pod ) {
			if (
				false !== strpos( $tags['fields'], '_capabilities' )
				|| false !== strpos( $tags['fields'], 'role' )
            ) {
				// Further hardening of User-based forms
				return pods_get_access_user_notice( $info, false, __( 'You cannot edit role or capabilities for users with Pods', 'pods' ) );
			} elseif ( $is_singular && ( ! defined( 'PODS_SHORTCODE_ALLOW_USER_EDIT' ) || ! PODS_SHORTCODE_ALLOW_USER_EDIT ) ) {
				// Only explicitly allow user edit forms
				return pods_get_access_user_notice( $info, false, __( 'Edit user profile forms have been disabled on this site.', 'pods' ) );
			}
		}

		$form_params = [
			'fields'       => $tags['fields'],
			'label'        => $tags['label'],
			'thank_you'    => $tags['thank_you'],
			'output_type'  => ! empty( $tags['form_output_type'] ) ? $tags['form_output_type'] : 'div',
			'form_key'     => $tags['form_key'],
			// We already checked the access so we can bypass this.
			'check_access' => false,
		];

		$return .= $pod->form( $form_params );

		$return = pods_wrap_html( $return, $tags );

		pods_debug_log_data( pods_array_filter_null_and_empty_string( $tags ), 'shortcode-final-args', __METHOD__, __LINE__ );

		/**
		 * Allow customization of shortcode output based on shortcode attributes.
		 *
		 * @since 2.7.9
		 *
		 * @param string    $return  Shortcode output to return.
		 * @param array     $tags    Shortcode attributes.
		 * @param null|Pods $pod     Pods object, or null if 'view' context.
		 * @param string    $context The shortcode context (form, field, pods-page, view, or list).
		 */
		return apply_filters( 'pods_shortcode_output', $return, $tags, $pod, 'form' );
	}

	// Handle field output.
	if ( ! empty( $tags['field'] ) ) {
		$link_field = $tags['link_field'] ?? null;

		if ( $tags['template'] || $content ) {
			$related = $pod->field( $tags['field'], array( 'output' => 'find' ) );

			if ( $related instanceof Pods && $related->valid() ) {
				// Content is null by default.
				$return_output = $related->template( $tags['template'], $content );

				if ( null !== $return_output ) {
					$return .= $return_output;
				}
			}
		} elseif ( empty( $tags['helper'] ) ) {
			$return_output = $pod->display( $tags['field'] );

			if ( null !== $return_output ) {
				$return .= $return_output;
			} else {
				$return = '';
			}
		} else {
			$return_output = $pod->helper( $tags['helper'], $pod->field( $tags['field'] ), $tags['field'] );

			if ( null !== $return_output ) {
				$return .= $return_output;
			} else {
				$return = '';
			}
		}

		// @todo $blog_is_switched >> Switch back before running other shortcodes?
		if ( $tags['shortcodes'] && defined( 'PODS_SHORTCODE_ALLOW_SUB_SHORTCODES' ) && PODS_SHORTCODE_ALLOW_SUB_SHORTCODES ) {
			$return = do_shortcode( $return );
		}

		// Maybe link the field if there's not HTML that would prevent that.
		if (
			$link_field
			&& false === strpos( $return, '<div' )
			&& false === strpos( $return, '<p' )
			&& false === strpos( $return, '<a' )
		) {
			$link_field_value = $pod->field( $link_field );

			if ( is_array( $link_field_value ) ) {
				if ( 0 < count( $link_field_value ) ) {
					$link_field_value = reset( $link_field_value );
				} else {
					$link_field_value = null;
				}
			}

			if (
				false !== strpos( $link_field_value, '://' )
				&& false === strpos( $link_field_value, ' ' )
			) {
				$return = sprintf( '<a href="%s">%s</a>', esc_url( $link_field_value ), $return );
			}
		}

		$return = pods_wrap_html( $return, $tags );

		pods_debug_log_data( pods_array_filter_null_and_empty_string( $tags ), 'shortcode-final-args', __METHOD__, __LINE__ );

		/**
		 * Allow customization of shortcode output based on shortcode attributes.
		 *
		 * @since 2.7.9
		 *
		 * @param string    $return  Shortcode output to return.
		 * @param array     $tags    Shortcode attributes.
		 * @param null|Pods $pod     Pods object, or null if 'view' context.
		 * @param string    $context The shortcode context (form, field, pods-page, view, or list).
		 */
		return apply_filters( 'pods_shortcode_output', $return, $tags, $pod, 'field' );
	}

	// Handle Pods Page output.
	if ( ! empty( $tags['pods_page'] ) && class_exists( 'Pods_Pages' ) ) {
		$pods_page = Pods_Pages::exists( $tags['pods_page'] );

		if ( empty( $pods_page ) ) {
			return pods_message(
				sprintf(
					'<strong>%1$s:</strong> %2$s',
					esc_html__( 'Pods Embed Error', 'pods' ),
					esc_html__( 'Pods Page not found.', 'pods' )
				),
				'error',
				true
			);
		}

		$return .= Pods_Pages::content( true, $pods_page );

		// @todo $blog_is_switched >> Switch back before running other shortcodes?
		if ( $tags['shortcodes'] && defined( 'PODS_SHORTCODE_ALLOW_SUB_SHORTCODES' ) && PODS_SHORTCODE_ALLOW_SUB_SHORTCODES ) {
			$return = do_shortcode( $return );
		}

		$return = pods_wrap_html( $return, $tags );

		pods_debug_log_data( pods_array_filter_null_and_empty_string( $tags ), 'shortcode-final-args', __METHOD__, __LINE__ );

		/**
		 * Allow customization of shortcode output based on shortcode attributes.
		 *
		 * @since 2.7.9
		 *
		 * @param string    $return  Shortcode output to return.
		 * @param array     $tags    Shortcode attributes.
		 * @param null|Pods $pod     Pods object, or null if 'view' context.
		 * @param string    $context The shortcode context (form, field, pods-page, view, or list).
		 */
		return apply_filters( 'pods_shortcode_output', $return, $tags, $pod, 'pods-page' );
	}//end if

	$pagination = false;

	// Only handle pagination on non-singular shortcodes where items were found.
	if (
		! $is_singular
		&& 0 < $found
		&& (
			empty( $params['limit'] )
			|| (
				0 < $params['limit']
				&& $params['limit'] < $found
			)
		)
		&& true === $tags['pagination']
	) {
		$pagination_params = [
			'label'           => pods_v( 'pagination_label', $tags, null ),
			'type'            => pods_v( 'pagination_type', $tags, null ),
			'total_found'     => $found,
			'wrap_pagination' => true,
		];

		// Remove empty params.
		$pagination_params = array_filter( $pagination_params );

		$pagination = $pod->pagination( $pagination_params );
	}

	ob_start();

	if ( $filters && 'before' === $tags['filters_location'] ) {
		// phpcs:ignore
		echo $filters;
	}

	if ( $pagination && in_array( $tags['pagination_location'], [ 'before', 'both' ], true ) ) {
		// phpcs:ignore
		echo $pagination;
	}

	$content = $pod->template( $tags['template'], $content, false, true );

	if ( '' === trim( $content ) && ! empty( $tags['not_found'] ) ) {
		$content = $pod->do_magic_tags( $tags['not_found'] );
	}

	// phpcs:ignore
	echo $content;

	if ( $pagination && in_array( $tags['pagination_location'], [ 'after', 'both' ], true ) ) {
		// phpcs:ignore
		echo $pagination;
	}

	if ( $filters && 'after' === $tags['filters_location'] ) {
		// phpcs:ignore
		echo $filters;
	}

	$return .= ob_get_clean();

	if ( $tags['shortcodes'] && defined( 'PODS_SHORTCODE_ALLOW_SUB_SHORTCODES' ) && PODS_SHORTCODE_ALLOW_SUB_SHORTCODES ) {
		$return = do_shortcode( $return );
	}

	$return = pods_wrap_html( $return, $tags );

	pods_debug_log_data( pods_array_filter_null_and_empty_string( $tags ), 'shortcode-final-args', __METHOD__, __LINE__ );

	/**
	 * Allow customization of shortcode output based on shortcode attributes.
	 *
	 * @since 2.7.9
	 *
	 * @param string    $return  Shortcode output to return.
	 * @param array     $tags    Shortcode attributes.
	 * @param null|Pods $pod     Pods object, or null if 'view' context.
	 * @param string    $context The shortcode context (form, field, pods-page, view, or list).
	 */
	return apply_filters( 'pods_shortcode_output', $return, $tags, $pod, 'list' );
}

/**
 * Form Shortcode support for use anywhere that support WP Shortcodes.
 *
 * @param array  $tags    An associative array of shortcode properties.
 * @param string $content Not currently used.
 *
 * @return string
 * @since 2.3.0
 */
function pods_shortcode_form( $tags, $content = null ) {
	$tags['form'] = 1;

	return pods_shortcode( $tags, $content );
}

/**
 * Fork of WordPress do_shortcode that allows specifying which shortcodes are ran.
 *
 * Search content for shortcodes and filter shortcodes through their hooks.
 *
 * If there are no shortcode tags defined, then the content will be returned
 * without any filtering. This might cause issues when plugins are disabled but
 * the shortcode will still show up in the post or content.
 *
 * @since 2.4.3
 *
 * @uses  $shortcode_tags
 * @uses  get_shortcode_regex() Gets the search pattern for searching shortcodes.
 *
 * @param string $content            Content to search for shortcodes.
 * @param array  $shortcodes         Array of shortcodes to run.
 * @param array  $ignored_shortcodes Array of shortcodes to ignore.
 *
 * @return string Content with shortcodes filtered out.
 */
function pods_do_shortcode( $content, $shortcodes = [], $ignored_shortcodes = [] ) {
	global $shortcode_tags;

	// No shortcodes in content
	if ( false === strpos( $content, '[' ) ) {
		return $content;
	}

	// No shortcodes registered
	if ( empty( $shortcode_tags ) || ! is_array( $shortcode_tags ) ) {
		return $content;
	}

	$only_shortcodes   = ! empty( $shortcodes );
	$ignore_shortcodes = ! empty( $ignored_shortcodes );

	if ( $only_shortcodes || $ignore_shortcodes ) {
		$temp_shortcode_filter = static function ( $return, $tag, $attr, $m ) use ( $only_shortcodes, $shortcodes, $ignore_shortcodes, $ignored_shortcodes ) {
			if ( $only_shortcodes && in_array( $m[2], $shortcodes, true ) ) {
				// If shortcode being called is in list, return false to allow it to run.
				return false;
			}

			if ( $ignore_shortcodes && ! in_array( $m[2], $ignored_shortcodes, true ) ) {
				// If shortcode being called is not in ignore list, return false to allow it to run.
				return false;
			}

			// Return original shortcode string if we aren't going to handle at this time
			return $m[0];
		};

		add_filter( 'pre_do_shortcode_tag', $temp_shortcode_filter, 10, 4 );
	}

	// Build Shortcode regex pattern just for the shortcodes we want
	$pattern = get_shortcode_regex();

	// Call shortcode callbacks just for the shortcodes we want
	$content = preg_replace_callback( "/$pattern/s", 'do_shortcode_tag', $content );

	if ( isset( $temp_shortcode_filter ) ) {
		remove_filter( 'pre_do_shortcode_tag', $temp_shortcode_filter );
	}

	return $content;
}

/**
 * Check if Pods is compatible with WP / PHP / MySQL or not
 *
 * @return bool
 *
 * @since 1.10
 */
function pods_compatibility_check() {
	$compatible = true;

	if ( ! pods_version_check( 'wp', PODS_WP_VERSION_MINIMUM ) ) {
		$compatible = false;

		add_action( 'admin_notices', 'pods_version_notice_wp' );
	}

	if ( ! pods_version_check( 'php', PODS_PHP_VERSION_MINIMUM ) ) {
		$compatible = false;

		add_action( 'admin_notices', 'pods_version_notice_php' );
	}

	if ( ! pods_version_check( 'mysql', PODS_MYSQL_VERSION_MINIMUM ) ) {
		$compatible = false;

		add_action( 'admin_notices', 'pods_version_notice_mysql' );
	}

	return $compatible;
}

/**
 * Show WP notice if WP version is incompatible
 *
 * @return void
 *
 * @since 1.10
 */
function pods_version_notice_wp() {
	global $wp_version;
	?>
	<div class="error fade">
		<p>
			<strong><?php _e( 'NOTICE', 'pods' ); ?>:</strong> Pods <?php echo esc_html( PODS_VERSION ); ?> <?php _e( 'requires a minimum of', 'pods' ); ?>
			<strong>WordPress <?php echo esc_html( PODS_WP_VERSION_MINIMUM ); ?>+</strong> <?php _e( 'to function. You are currently running', 'pods' ); ?>
			<strong>WordPress <?php echo esc_html( $wp_version ); ?></strong> - <?php _e( 'Please upgrade your WordPress to continue.', 'pods' ); ?>
		</p>
	</div>
	<?php
}

/**
 * Show WP notice if PHP version is incompatible
 *
 * @return void
 *
 * @since 1.10
 */
function pods_version_notice_php() {
	?>
	<div class="error fade">
		<p>
			<strong><?php _e( 'NOTICE', 'pods' ); ?>:</strong> Pods <?php echo esc_html( PODS_VERSION ); ?> <?php _e( 'requires a minimum of', 'pods' ); ?>
			<strong>PHP <?php echo esc_html( PODS_PHP_VERSION_MINIMUM ); ?>+</strong> <?php _e( 'to function. You are currently running', 'pods' ); ?>
			<strong>PHP <?php echo esc_html( phpversion() ); ?></strong> - <?php _e( 'Please upgrade (or have your Hosting Provider upgrade it for you) your PHP version to continue.', 'pods' ); ?>
		</p>
	</div>
	<?php
}

/**
 * Show WP notice if MySQL version is incompatible
 *
 * @return void
 *
 * @since 1.10
 */
function pods_version_notice_mysql() {
	global $wpdb;
	$mysql = $wpdb->db_version();
	?>
	<div class="error fade">
		<p>
			<strong><?php _e( 'NOTICE', 'pods' ); ?>:</strong> Pods <?php echo esc_html( PODS_VERSION ); ?> <?php _e( 'requires a minimum of', 'pods' ); ?>
			<strong>MySQL <?php echo esc_html( PODS_MYSQL_VERSION_MINIMUM ); ?>+</strong> <?php _e( 'to function. You are currently running', 'pods' ); ?>
			<strong>MySQL <?php echo esc_html( $mysql ); ?></strong> - <?php _e( 'Please upgrade (or have your Hosting Provider upgrade it for you) your MySQL version to continue.', 'pods' ); ?>
		</p>
	</div>
	<?php
}

/**
 * Check if a Function exists or File exists in Theme / Child Theme
 *
 * @param string $function_or_file Function or file name to look for.
 * @param string $function_name    (optional) Function name to look for.
 * @param string $file_dir         (optional) Drectory to look into
 * @param string $file_name        (optional) Filename to look for
 *
 * @return mixed
 *
 * @since 1.12
 */
function pods_function_or_file( $function_or_file, $function_name = null, $file_dir = null, $file_name = null ) {
	$found            = false;
	$function_or_file = (string) $function_or_file;
	if ( false !== $function_name ) {
		if ( null === $function_name ) {
			$function_name = $function_or_file;
		}
		$function_name = str_replace( array(
			'__',
			'__',
			'__',
		), '_', preg_replace( '/[^a-z^A-Z^_][^a-z^A-Z^0-9^_]*/', '_', (string) $function_name ) );
		if ( function_exists( 'pods_custom_' . $function_name ) ) {
			$found = array( 'function' => 'pods_custom_' . $function_name );
		} elseif ( function_exists( $function_name ) ) {
			$found = array( 'function' => $function_name );
		}
	}
	if ( false !== $file_name && false === $found ) {
		if ( null === $file_name ) {
			$file_name = $function_or_file;
		}
		$file_name       = str_replace( array(
				'__',
				'__',
				'__',
			), '_', preg_replace( '/[^a-z^A-Z^0-9^_]*/', '_', (string) $file_name ) ) . '.php';
		$custom_location = apply_filters( 'pods_file_directory', null, $function_or_file, $function_name, $file_dir, $file_name );
		if ( defined( 'PODS_FILE_DIRECTORY' ) && false !== PODS_FILE_DIRECTORY ) {
			$custom_location = PODS_FILE_DIRECTORY;
		}
		if ( ! empty( $custom_location ) && locate_template( trim( $custom_location, '/' ) . '/' . ( ! empty( $file_dir ) ? $file_dir . '/' : '' ) . $file_name ) ) {
			$found = array( 'file' => trim( $custom_location, '/' ) . '/' . ( ! empty( $file_dir ) ? $file_dir . '/' : '' ) . $file_name );
		} elseif ( locate_template( 'pods/' . ( ! empty( $file_dir ) ? $file_dir . '/' : '' ) . $file_name ) ) {
			$found = array( 'file' => 'pods/' . ( ! empty( $file_dir ) ? $file_dir . '/' : '' ) . $file_name );
		} elseif ( locate_template( 'pods-' . ( ! empty( $file_dir ) ? $file_dir . '-' : '' ) . $file_name ) ) {
			$found = array( 'file' => 'pods-' . ( ! empty( $file_dir ) ? $file_dir . '-' : '' ) . $file_name );
		} elseif ( locate_template( 'pods/' . ( ! empty( $file_dir ) ? $file_dir . '-' : '' ) . $file_name ) ) {
			$found = array( 'file' => 'pods/' . ( ! empty( $file_dir ) ? $file_dir . '-' : '' ) . $file_name );
		}
	}//end if

	return apply_filters( 'pods_function_or_file', $found, $function_or_file, $function_name, $file_name );
}

/**
 * Redirects to another page.
 *
 * @since 2.0.0
 *
 * @param string|null $location The path to redirect to.
 * @param int         $status   Status code to use.
 * @param boolean     $die      If true, PHP code exection will stop.
 */
function pods_redirect( $location = null, $status = 302, $die = true ) {
	if ( empty( $location ) ) {
		$location = $_SERVER['REQUEST_URI'];
	}

	if ( ! headers_sent() ) {
		wp_redirect( $location, $status );
		if ( $die ) {
			die();
		}
	} else {
		echo '<script type="text/javascript">' . 'document.location = "' . str_replace( '&amp;', '&', esc_js( $location ) ) . '";' . '</script>';
		if ( $die ) {
			die();
		}
	}
}

/**
 * Check if a user has permission to be doing something based on standard permission options
 *
 * @param array|Whatsit $object The object data.
 *
 * @return bool Whether the user has permissions.
 *
 * @since 2.0.5
 */
function pods_permission( $object ) {
	$permissions = pods_container( Permissions::class );

	return $permissions->user_has_permission( $object );
}

/**
 * Check if permissions are restricted for an object.
 *
 * @since 2.3.4
 *
 * @param array|Whatsit $object The object data.
 *
 * @return bool Whether the permissions are restricted for an object.
 */
function pods_has_permissions( $object ) {
	$permissions = pods_container( Permissions::class );

	return $permissions->are_permissions_restricted( $object );
}

/**
 * A fork of get_page_by_title that excludes items unavailable via access rights (by status)
 *
 * @see   get_page_by_title
 *
 * @param string       $title   Title of item to get
 * @param string       $output  Optional. Output type. OBJECT, ARRAY_N, or ARRAY_A. Default OBJECT.
 * @param string       $type    Post Type
 * @param string|array $status  Post statuses to include (default is what user has access to)
 * @param bool         $return  Whether to return the 'id' or 'post'.
 *
 * @return WP_Post|int|null WP_Post on success or null on failure
 *
 * @since 2.3.4
 */
function pods_by_title( $title, $output = OBJECT, $type = 'page', $status = null, $return = 'post' ) {
	// @todo support Pod item lookups, not just Post Types
	/**
	 * @var $wpdb WPDB
	 */
	global $wpdb;

	if ( empty( $status ) ) {
		$status = array(
			'publish',
		);

		if ( current_user_can( 'read_private_' . $type . 's' ) ) {
			$status[] = 'private';
		}

		if ( current_user_can( 'edit_' . $type . 's' ) ) {
			$status[] = 'draft';
		}
	}

	$status = (array) $status;

	$status_sql = ' AND `post_status` IN ( %s' . str_repeat( ', %s', count( $status ) - 1 ) . ' )';

	$orderby_sql = ' ORDER BY ( `post_status` = %s ) DESC' . str_repeat( ', ( `post_status` = %s ) DESC', count( $status ) - 1 ) . ', `ID` DESC';

	// Once for WHERE, once for ORDER BY
	$prepared = array_merge( array( $title, $type ), $status, $status );

	$page = (int) $wpdb->get_var( $wpdb->prepare( "SELECT `ID` FROM `{$wpdb->posts}` WHERE `post_title` = %s AND `post_type` = %s" . $status_sql . $orderby_sql, $prepared ) );

	if ( 0 < $page ) {
		if ( 'id' === $return ) {
			return pods_v( $page, 'post_id' );
		}

		return get_post( $page, $output );
	}

	return null;
}

/**
 * Get a field value from a Pod.
 *
 * @param string|null  $pod    The pod name, or if you are in The Loop then you can just provide the field name to auto-detect pod/id using loop information.
 * @param mixed|null   $id     The ID or slug of the item.
 * @param string|array $name   The field name, or an associative array of parameters.
 * @param boolean      $single For tableless fields, to return the whole array or the just the first item.
 *
 * @return mixed Field value.
 *
 * @since 2.1.0
 */
function pods_field( $pod, $id = null, $name = null, $single = false ) {
	// allow for pods_field( 'field_name' );
	if ( null === $name ) {
		$name   = $pod;
		$single = (boolean) $id;

		$pod = null;
		$id  = null;
	}

	if ( null === $pod && null === $id ) {
		$pod = get_post_type();
		$id  = get_the_ID();
	}

	$pod_object = pods( $pod, $id );

	if ( is_object( $pod_object ) && $pod_object->exists() ) {
		return $pod_object->field( $name, $single );
	}

	return null;
}

/**
 * Get the data field value.
 *
 * @since 2.9.4
 *
 * @param Pods|string|null $obj        The pod name or Pods object.
 * @param string           $field_name The field name.
 *
 * @return mixed The data field value.
 */
function pods_data_field( $obj, $field_name ) {
	if ( is_string( $obj ) ) {
		$obj = pods( $obj );
	}

	$traverse_fields = explode( '.', $field_name );
	$is_traversal    = 1 < count( $traverse_fields );
	$first_field     = $traverse_fields[0];

	// Get the first field name data.
	$field_data = $obj ? $obj->fields( $first_field ) : null;

	// Ensure the field name is using the correct name and not the alias.
	if ( $field_data ) {
		$first_field = $field_data['name'];

		if ( ! $is_traversal ) {
			$field_name = $first_field;
		}
	}

	if ( $obj && ! $field_data && ! $is_traversal ) {
		// Get the full field name data.
		$field_data = $obj->fields( $field_name );
	}

	$is_field_set = false;

	if ( $field_data instanceof Object_Field ) {
		$is_field_set = true;
	} elseif ( $field_data instanceof Field ) {
		$is_field_set = true;
	}

	// Handle custom/supported value mappings.
	$map_field_values = pods_container( Map_Field_Values::class );

	return $map_field_values->map_value( $first_field, $traverse_fields, $is_field_set ? $field_data : null, $obj );
}

/**
 * Get a field display value from a Pod.
 *
 * @param string|null  $pod    The pod name, or if you are in The Loop then you can just provide the field name to auto-detect pod/id using loop information.
 * @param mixed|null   $id     The ID or slug of the item.
 * @param string|array $name   The field name, or an associative array of parameters.
 * @param boolean      $single For tableless fields, to return the whole array or the just the first item.
 *
 * @return mixed Field value.
 *
 * @since 2.1.0
 */
function pods_field_display( $pod, $id = null, $name = null, $single = false ) {
	// allow for pods_field_display( 'field_name' );
	if ( null === $name ) {
		$name   = $pod;
		$single = (boolean) $id;

		$pod = null;
		$id  = null;
	}

	if ( null === $pod && null === $id ) {
		$pod = get_post_type();
		$id  = get_the_ID();
	}

	$pod_object = pods( $pod, $id );

	if ( is_object( $pod_object ) && $pod_object->exists() ) {
		return $pod_object->display( $name, $single );
	}

	return null;
}

/**
 * Get a field raw value from a Pod.
 *
 * @param string|null  $pod    The pod name.
 * @param mixed|null   $id     The ID or slug of the item.
 * @param string|array $name   The field name, or an associative array of parameters.
 * @param boolean      $single For tableless fields, to return the whole array or the just the first item.
 *
 * @return mixed Field value.
 *
 * @since 2.1.0
 */
function pods_field_raw( $pod, $id = null, $name = null, $single = false ) {
	// allow for pods_field_raw( 'field_name' );
	if ( null === $name ) {
		$name   = $pod;
		$single = (boolean) $id;

		$pod = null;
		$id  = null;
	}

	if ( null === $pod && null === $id ) {
		$pod = get_post_type();
		$id  = get_the_ID();
	}

	$pod_object = pods( $pod, $id );

	if ( is_object( $pod_object ) && $pod_object->exists() ) {
		return $pod_object->raw( $name, $single );
	}

	return null;

}

/**
 * Update a field value for a Pod.
 *
 * @param string|null  $pod   The pod name.
 * @param mixed|null   $id    The ID or slug of the item.
 * @param string|array $name  The field name, or an associative array of parameters.
 * @param boolean      $value Value to save.
 *
 * @return int|false The item ID or false if not saved.
 *
 * @since 2.7.17
 */
function pods_field_update( $pod, $id = null, $name = null, $value = null ) {

	// allow for pods_field( 'field_name' );
	if ( null === $name ) {
		$name  = $pod;
		$value = $id;

		$pod = null;
		$id  = null;
	}

	if ( null === $pod && null === $id ) {
		$pod = get_post_type();
		$id  = get_the_ID();
	}

	$pod_object = pods( $pod, $id );

	if ( is_object( $pod_object ) && $pod_object->exists() ) {
		return $pod_object->save( $name, $value );
	}

	return false;
}

/**
 * Set a cached value
 *
 * @see   PodsView::set
 *
 * @param string $key        Key for the cache
 * @param mixed  $value      Value to add to the cache
 * @param int    $expires    (optional) Time in seconds for the cache to expire, if 0 no expiration.
 * @param string $cache_mode (optional) Decides the caching method to use for the view.
 * @param string $group      (optional) Key for the group
 *
 * @return bool|mixed|null|string|void
 *
 * @since 2.0.0
 */
function pods_view_set( $key, $value, $expires = 0, $cache_mode = 'cache', $group = '' ) {
	return PodsView::set( $key, $value, $expires, $cache_mode, $group );
}

/**
 * Get a cached value
 *
 * @see   PodsView::get
 *
 * @param string $key        Key for the cache
 * @param string $cache_mode (optional) Decides the caching method to use for the view.
 * @param string $group      (optional) Key for the group
 * @param string $callback   (optional) Callback function to run to set the value if not cached
 *
 * @return bool|mixed|null|void
 *
 * @since 2.0.0
 */
function pods_view_get( $key, $cache_mode = 'cache', $group = '', $callback = null ) {
	return PodsView::get( $key, $cache_mode, $group, $callback );
}

/**
 * Clear a cached value
 *
 * @see   PodsView::clear
 *
 * @param string|bool $key        Key for the cache
 * @param string      $cache_mode (optional) Decides the caching method to use for the view.
 * @param string      $group      (optional) Key for the group
 *
 * @return bool
 *
 * @since 2.0.0
 */
function pods_view_clear( $key = true, $cache_mode = 'cache', $group = '' ) {
	return PodsView::clear( $key, $cache_mode, $group );
}

/**
 * Set a cached value
 *
 * @see   PodsView::set
 *
 * @param string $key     Key for the cache
 * @param mixed  $value   Value to add to the cache
 * @param string $group   (optional) Key for the group
 * @param int    $expires (optional) Time in seconds for the cache to expire, if 0 no expiration.
 *
 * @return bool|mixed|null|string|void
 *
 * @since 2.0.0
 */
function pods_cache_set( $key, $value, $group = '', $expires = 0 ) {
	return pods_view_set( $key, $value, $expires, 'cache', $group );
}

/**
 * Get a cached value
 *
 * @see   PodsView::get
 *
 * @param string $key      Key for the cache
 * @param string $group    (optional) Key for the group
 * @param string $callback (optional) Callback function to run to set the value if not cached
 *
 * @return bool
 *
 * @since 2.0.0
 */
function pods_cache_get( $key, $group = '', $callback = null ) {
	return pods_view_get( $key, 'cache', $group, $callback );
}

/**
 * Clear a cached value
 *
 * @see   PodsView::clear
 *
 * @param string|bool $key   Key for the cache
 * @param string      $group (optional) Key for the group
 *
 * @return bool|mixed|null|void
 *
 * @since 2.0.0
 */
function pods_cache_clear( $key = true, $group = '' ) {
	return pods_view_clear( $key, 'cache', $group );
}

/**
 * Set a cached value
 *
 * @see   PodsView::set
 *
 * @param string $key     Key for the cache
 * @param mixed  $value   Value to add to the cache
 * @param int    $expires (optional) Time in seconds for the cache to expire, if 0 no expiration.
 *
 * @return bool|mixed|null|string|void
 *
 * @since 2.0.0
 */
function pods_transient_set( $key, $value, $expires = 0 ) {
	return pods_view_set( $key, $value, $expires, 'transient' );
}

/**
 * Get a cached value
 *
 * @see   PodsView::get
 *
 * @param string $key      Key for the cache
 * @param string $callback (optional) Callback function to run to set the value if not cached
 *
 * @return bool|mixed|null|void
 *
 * @since 2.0.0
 */
function pods_transient_get( $key, $callback = null ) {
	return pods_view_get( $key, 'transient', '', $callback );
}

/**
 * Clear a cached value
 *
 * @see   PodsView::clear
 *
 * @param string|bool $key Key for the cache
 *
 * @return bool
 *
 * @since 2.0.0
 */
function pods_transient_clear( $key = true ) {
	return pods_view_clear( $key, 'transient' );
}

/**
 * Set a cached value
 *
 * @see   PodsView::set
 *
 * @param string $key     Key for the cache
 * @param mixed  $value   Value to add to the cache
 * @param int    $expires (optional) Time in seconds for the cache to expire, if 0 no expiration.
 *
 * @return bool|mixed|null|string|void
 *
 * @since 2.3.10
 */
function pods_site_transient_set( $key, $value, $expires = 0 ) {
	return pods_view_set( $key, $value, $expires, 'site-transient' );
}

/**
 * Get a cached value
 *
 * @see   PodsView::get
 *
 * @param string $key      Key for the cache
 * @param string $callback (optional) Callback function to run to set the value if not cached
 *
 * @return bool|mixed|null|void
 *
 * @since 2.3.10
 */
function pods_site_transient_get( $key, $callback = null ) {
	return pods_view_get( $key, 'site-transient', '', $callback );
}

/**
 * Clear a cached value
 *
 * @see   PodsView::clear
 *
 * @param string|bool $key Key for the cache
 *
 * @return bool
 *
 * @since 2.3.10
 */
function pods_site_transient_clear( $key = true ) {
	return pods_view_clear( $key, 'site-transient' );
}

/**
 * Set a cached value
 *
 * @see   PodsView::set
 *
 * @param string $key     Key for the cache
 * @param mixed  $value   Value to add to the cache
 * @param int    $expires (optional) Time in seconds for the cache to expire, if 0 no expiration.
 * @param string $group   (optional) Key for the group
 *
 * @return bool|mixed|null|string|void
 *
 * @since 2.3.10
 */
function pods_option_cache_set( $key, $value, $expires = 0, $group = '' ) {
	return pods_view_set( $key, $value, $expires, 'option-cache', $group );
}

/**
 * Get a cached value
 *
 * @see   PodsView::get
 *
 * @param string $key      Key for the cache
 * @param string $group    (optional) Key for the group
 * @param string $callback (optional) Callback function to run to set the value if not cached
 *
 * @return bool|mixed|null|void
 *
 * @since 2.3.10
 */
function pods_option_cache_get( $key, $group = '', $callback = null ) {
	return pods_view_get( $key, 'option-cache', $group, $callback );
}

/**
 * Clear a cached value
 *
 * @see   PodsView::clear
 *
 * @param string|bool $key   Key for the cache
 * @param string      $group (optional) Key for the group
 *
 * @return bool
 *
 * @since 2.3.10
 */
function pods_option_cache_clear( $key = true, $group = '' ) {
	return pods_view_clear( $key, 'option-cache', $group );
}

/**
 * Set a cached value in the Pods Static Cache.
 *
 * @see   PodsView::set
 *
 * @param string $key   Key for the cache.
 * @param mixed  $value Value to add to the cache.
 * @param string $group (optional) Key for the group.
 *
 * @return bool|mixed|null|string|void
 *
 * @since 2.8.18
 */
function pods_static_cache_set( $key, $value, $group = '' ) {
	return pods_view_set( $key, $value, 0, 'static-cache', $group );
}

/**
 * Get a cached value in the Pods Static Cache.
 *
 * @see   PodsView::get
 *
 * @param string $key      Key for the cache.
 * @param string $group    (optional) Key for the group.
 * @param string $callback (optional) Callback function to run to set the value if not cached.
 *
 * @return bool
 *
 * @since 2.8.18
 */
function pods_static_cache_get( $key, $group = '', $callback = null ) {
	return pods_view_get( $key, 'static-cache', $group, $callback );
}

/**
 * Clear a cached value in the Pods Static Cache.
 *
 * @see PodsView::clear
 *
 * @param string|bool $key   Key for the cache.
 * @param string      $group (optional) Key for the group.
 *
 * @return bool|mixed|null|void
 *
 * @since 2.8.18
 */
function pods_static_cache_clear( $key = true, $group = '' ) {
	return pods_view_clear( $key, 'static-cache', $group );
}

/**
 * Scope variables and include a template like get_template_part that's child-theme aware
 *
 * @see   get_template_part
 *
 * @param string|array $template Template names (see get_template_part)
 * @param array        $data     Data to scope to the include
 * @param bool         $return   Whether to return the output (echo by default)
 *
 * @return string|null Template output
 *
 * @since 2.3.9
 */
function pods_template_part( $template, $data = null, $return = false ) {
	$part = PodsView::get_template_part( $template, $data );

	if ( ! $return ) {
		echo $part;

		return null;
	}

	return $part;
}

/**
 * Add a new Pod outside of the DB.
 *
 * @param string $type   The pod type ('post_type', 'taxonomy', 'media', 'user', 'comment')
 * @param string $name   The pod name
 * @return array|boolean|WP_Error Field data or WP_Error if unsuccessful.
 *
 * @return array|boolean Pod data or false if unsuccessful
 * @since 2.1.0
 */
function pods_register_type( $type, $name, $object = null ) {
	if ( empty( $object ) ) {
		$object = [];
	}

	if ( ! empty( $name ) ) {
		$object['name'] = $name;
	}

	if ( ! empty( $type ) ) {
		$object['type'] = $type;
	}

	if ( ! isset( PodsMeta::$queue[ $object['type'] ] ) ) {
		PodsMeta::$queue[ $object['type'] ] = [];
	}

	$groups = [];
	$fields = [];

	if ( isset( $object['groups'] ) ) {
		$groups = $object['groups'];

		unset( $object['groups'] );
	}

	if ( isset( $object['fields'] ) ) {
		$fields = $object['fields'];

		unset( $object['fields'] );
	}

	// Maybe set the code source for this type.
	if (
		! isset( $object['_pods_code_source'] )
		&& (
			! isset( $object['object_storage_type'] )
			|| 'file' !== $object['object_storage_type']
		)
	) {
		$debug_info = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS, 3 );

		foreach ( $debug_info as $debug ) {
			// Skip Pods-related and WP-related hook registrations.
			if ( 0 === strpos( $debug['file'], PODS_DIR ) || 0 === strpos( $debug['file'], WPINC ) ) {
				continue;
			}

			$object['_pods_code_source'] = sprintf(
				'%s : %s %d',
				$debug['file'],
				esc_html__( 'Line', 'pods' ),
				$debug['line']
			);

			break;
		}
	}

	$registered = pods_register_object( $object, 'pod' );

	if ( true === $registered ) {
		try {
			$object_collection = Store::get_instance();

			$registered_object = $object_collection->get_object( 'pod/' . $object['name'] );

			if ( $registered_object ) {
				PodsMeta::$queue[ $object['type'] ][ $object['name'] ] = $registered_object;
			}
		} catch ( Exception $exception ) {
			return new WP_Error( 'pods-register-type-error', $exception->getMessage() );
		}
	}

	foreach ( $groups as $group ) {
		pods_register_group( $group, $object['name'] );
	}

	foreach ( $fields as $field ) {
		pods_register_field( $object['name'], $field['name'], $field );
	}

	pods_meta()->setup_hooks( $object );

	return $registered;
}

/**
 * Add a new Pod field outside of the DB.
 *
 * @see   PodsMeta::register_field
 *
 * @param string|array $pod    The pod name or array of pod names
 * @param string       $name   The name of the Pod
 * @param array        $object (optional) Pod array, including any 'fields' arrays
 *
 * @return array|boolean|WP_Error Field data or WP_Error if unsuccessful.
 * @since 2.1.0
 */
function pods_register_field( $pod, $name, $field = null ) {
	$pod_name = is_string( $pod ) ? $pod : $pod['name'];

	if ( empty( $field ) ) {
		$field = [];
	}

	if ( ! empty( $name ) ) {
		$field['name'] = $name;
	}

	$field['parent'] = 'pod/' . $pod_name;

	return pods_register_object( $field, 'field' );
}

/**
 * Add a new Pod field type
 *
 * @see   PodsForm::register_field_type
 *
 * @param string $type The new field type identifier
 * @param string $file The new field type class file location
 *
 * @return array Field type array
 * @since 2.3.0
 */
function pods_register_field_type( $type, $file = null ) {
	return PodsForm::register_field_type( $type, $file );
}

/**
 * Register a related object
 *
 * @param string $name    Object name
 * @param string $label   Object label
 * @param array  $options Object options
 *
 * @return array|boolean Object array or false if unsuccessful
 * @since 2.3.0
 */
function pods_register_related_object( $name, $label, $options = null ) {
	return PodsForm::field_method( 'pick', 'register_related_object', $name, $label, $options );
}

/**
 * Register an object with Pods.
 *
 * @since 2.8.0
 *
 * @param array  $object The object configuration.
 * @param string $type   The object type.
 *
 * @return true|WP_Error True if successful, or else an WP_Error with the problem.
 */
function pods_register_object( array $object, $type ) {
	$object['object_type'] = $type;

	if ( ! isset( $object['object_storage_type'] ) || 'post_type' === $object['object_storage_type'] ) {
		$object['object_storage_type'] = 'collection';
	}

	try {
		$object_collection = Store::get_instance();
		$object_collection->register_object( $object );
	} catch ( Exception $exception ) {
		return new WP_Error( 'pods-register-object-error', $exception->getMessage() );
	}

	return true;
}

/**
 * Register a group and it's fields with Pods.
 *
 * @since 2.8.0
 *
 * @param array  $group The group configuration.
 * @param string $pod   The pod to register to.
 * @param array  $field The list of group fields.
 *
 * @return true|WP_Error True if successful, or else an WP_Error with the problem.
 */
function pods_register_group( array $group, $pod, array $fields = [] ) {
	$pod_name = is_string( $pod ) ? $pod : $pod['name'];

	$group['parent'] = 'pod/' . $pod_name;

	if ( isset( $group['fields'] ) ) {
		$fields = $group['fields'];

		unset( $group['fields'] );
	}

	pods_register_object( $group, 'group' );

	foreach ( $fields as $field ) {
		pods_register_group_field( $field, $group['name'], $pod );
	}

	return true;
}

/**
 * Register a field with Pods.
 *
 * @since 2.8.0
 *
 * @param array  $field The field configuration.
 * @param string $group The group to register to.
 * @param string $pod   The pod to register to.
 *
 * @return true|WP_Error True if successful, or else an WP_Error with the problem.
 */
function pods_register_group_field( array $field, $group, $pod ) {
	$pod_name   = is_string( $pod ) ? $pod : $pod['name'];
	$group_name = is_string( $group ) ? $group : $group['name'];

	$field['parent'] = 'pod/' . $pod_name;
	$field['group']  = $group_name;

	pods_register_object( $field, 'field' );

	return true;
}

/**
 * Register a block type with Pods. Always register during the `pods_blocks_api_init` action.
 *
 * @since 2.8.0
 *
 * @param array $block  The block configuration, compatible with `register_block_type()`.
 * @param array $fields List of fields to use for inspector controls.
 *
 * @return true|WP_Error True if successful, or else an WP_Error with the problem.
 *
 * @see register_block_type
 * @see Pods\Blocks\Types\Base
 */
function pods_register_block_type( array $block, array $fields = [] ) {
	if ( empty( $block['namespace'] ) ) {
		return new WP_Error( 'pods-blocks-api-block-type-invalid', __( 'Invalid block type configuration provided', 'pods' ) );
	}

	$block['object_type']  = 'block';
	$block['object_storage_type'] = 'collection';
	$block['name']         = pods_v( 'name', $block, pods_v( 'slug', $block ) );
	$block['label']        = pods_v( 'label', $block, pods_v( 'title', $block ) );
	$block['category']     = pods_v( 'category', $block, pods_v( 'collection', $block ) );

	$object_collection = Store::get_instance();
	$object_collection->register_object( $block );

	foreach ( $fields as $field ) {
		$field['object_type']  = 'block-field';
		$field['object_storage_type'] = 'collection';
		$field['parent']       = 'block/' . $block['name'];
		$field['name']         = pods_v( 'name', $field, pods_v( 'slug', $field ) );
		$field['label']        = pods_v( 'label', $field, pods_v( 'title', $field ) );

		$object_collection->register_object( $field );
	}

	pods_static_cache_clear( 'get_blocks', \Pods\Blocks\API::class );

	return true;
}

/**
 * Register a block collection with Pods. Always register during the `pods_blocks_api_init` action.
 *
 * @since 2.8.0
 *
 * @param array $collection The block collection configuration, compatible with `block_categories` filter.
 *
 * @return true|WP_Error True if successful, or else an WP_Error with the problem.
 *
 * @see Pods\Blocks\Collections\Base
 */
function pods_register_block_collection( array $collection ) {
	if ( empty( $collection['namespace'] ) ) {
		return new WP_Error( 'pods-blocks-api-block-collection-invalid', __( 'Invalid block collection configuration provided', 'pods' ) );
	}

	$collection['object_type']  = 'block-collection';
	$collection['object_storage_type'] = 'collection';
	$collection['label']        = pods_v( 'label', $collection, pods_v( 'title', $collection ) );

	$object_collection = Store::get_instance();
	$object_collection->register_object( $collection );

	return true;
}

/**
 * Register a custom config file to use with Pods configs.
 *
 * @since 2.9.0
 *
 * @param string $file        The config file to use.
 * @param string $config_type The config file type to use (defaults to json).
 */
function pods_register_config_file( $file, $config_type = 'json' ) {
	try {
		$config_handler = pods_container( Config_Handler::class );

		$config_handler->register_file( $file, $config_type );
	} catch ( Exception $exception ) {
		// Container does not exist yet, we cannot do anything at this point.
	}
}

/**
 * Register a custom config path to use with Pods configs.
 *
 * @since 2.9.0
 *
 * @param string $path The config path to use.
 */
function pods_register_config_path( $path ) {
	try {
		$config_handler = pods_container( Config_Handler::class );

		$config_handler->register_path( $path );
	} catch ( Exception $exception ) {
		// Container does not exist yet, we cannot do anything at this point.
	}
}

/**
 * Register a custom config type to use with Pods configs.
 *
 * For custom config types, use the pods_config_parse_$type filter along with this to support other format parsing.
 *
 * Default support for json and yml can be filtered with the pods_config_parse filter to override them.
 *
 * @since 2.9.0
 *
 * @param string $type The config type to use.
 */
function pods_register_config_type( $type ) {
	try {
		$config_handler = pods_container( Config_Handler::class );

		$config_handler->register_config_type( $type );
	} catch ( Exception $exception ) {
		// Container does not exist yet, we cannot do anything at this point.
	}
}

/**
 * Register a custom config item type to use with Pods configs.
 *
 * @since 2.9.0
 *
 * @param string $item_type The config path to use.
 */
function pods_register_config_item_type( $item_type ) {
	try {
		$config_handler = pods_container( Config_Handler::class );

		$config_handler->register_config_item_type( $item_type );
	} catch ( Exception $exception ) {
		// Container does not exist yet, we cannot do anything at this point.
	}
}

/**
 * Require a component (always-on)
 *
 * @param string $component Component ID
 *
 * @return void
 *
 * @since 2.3.0
 */
function pods_require_component( $component ) {
	add_filter( 'pods_component_require_' . $component, '__return_true' );
}

/**
 * Add a meta group of fields to add/edit forms
 *
 * @see   PodsMeta::group_add
 *
 * @param string|array $pod      The pod or type of element to attach the group to.
 * @param string       $label    Title of the edit screen section, visible to user.
 * @param string|array $fields   Either a comma separated list of text fields or an associative array containing field
 *                               information.
 * @param string       $context  (optional) The part of the page where the edit screen section should be shown
 *                               ('normal', 'advanced', or 'side').
 * @param string       $priority (optional) The priority within the context where the boxes should show ('high',
 *                               'core', 'default' or 'low').
 * @param string       $type     (optional) Type of the post to attach to.
 *
 * @return void
 *
 * @since 2.0.0
 * @link  https://docs.pods.io/code/general-functions/pods-group-add/
 */
function pods_group_add( $pod, $label, $fields, $context = 'normal', $priority = 'default', $type = null ) {
	pods_meta()->group_add( $pod, $label, $fields, $context, $priority );
}

/**
 * Check if a plugin is active on non-admin pages (is_plugin_active() only available in admin)
 *
 * @param string $plugin Plugin name.
 *
 * @return bool
 *
 * @since 2.0.0
 */
function pods_is_plugin_active( $plugin ) {
	$active = false;

	if ( function_exists( 'is_plugin_active' ) ) {
		$active = is_plugin_active( $plugin );
	}

	if ( ! $active ) {
		$active_plugins = (array) get_option( 'active_plugins', array() );

		if ( in_array( $plugin, $active_plugins, true ) ) {
			$active = true;
		}

		if ( ! $active && is_multisite() ) {
			$plugins = get_site_option( 'active_sitewide_plugins' );

			if ( isset( $plugins[ $plugin ] ) ) {
				$active = true;
			}
		}
	}

	return $active;
}

/**
 * Check if Pods no conflict is on or not.
 *
 * Note: $object_type in the future may be default to null instead of 'post'.
 *
 * @param string|null $object_type The object type to check if no conflict is on for.
 *
 * @return bool Whether no conflict is on.
 *
 * @since 2.3.0
 */
function pods_no_conflict_check( $object_type = 'post' ) {
	if ( 'post_type' === $object_type ) {
		$object_type = 'post';
	} elseif ( 'term' === $object_type ) {
		$object_type = 'taxonomy';
	}

	if ( ! class_exists( 'PodsInit' ) ) {
		pods_init();
	}

	if ( empty( $object_type ) ) {
		return ! empty( PodsInit::$no_conflict );
	}

	if ( ! empty( PodsInit::$no_conflict[ $object_type ] ) ) {
		return true;
	}

	return false;
}

/**
 * Get the list of meta hooks to add/remove for a specific object type.
 *
 * @since 2.8.0
 *
 * @param string      $object_type The object type.
 * @param string|null $object      The object name.
 *
 * @return array List of filters and actions for a specific object type.
 */
function pods_meta_hook_list( $object_type = 'post', $object = null ) {
	if ( 'post_type' === $object_type ) {
		$object_type = 'post';
	} elseif ( 'term' === $object_type ) {
		$object_type = 'taxonomy';
	}

	$hooks = [
		'filter' => [],
		'action' => [],
	];

	// If Pods is not being used for any fields, bypass all hooks not needed.
	$is_types_only = pods_is_types_only();

	$first_pods_version = get_option( 'pods_framework_version_first' );
	$first_pods_version = '' === $first_pods_version ? PODS_VERSION : $first_pods_version;

	$metadata_integration = ! $is_types_only && 1 === (int) pods_get_setting( 'metadata_integration', ( function_exists( 'wc_get_product' ) || version_compare( $first_pods_version, '2.9.14', '<' ) ) ? '1' : '0' );
	$watch_changed_fields = ! $is_types_only && 1 === (int) pods_get_setting( 'watch_changed_fields', version_compare( $first_pods_version, '2.8.21', '<=' ) ? 1 : 0 );
	$media_modal_fields   = ! $is_types_only && 1 === (int) pods_get_setting( 'media_modal_fields', version_compare( $first_pods_version, '2.9.16', '<=' ) ? 1 : 0 );

	$is_tableless = pods_tableless();

	// Filters = Usually get/update/delete meta functions
	// Actions = Usually insert/update/save/delete object functions
	if ( 'post' === $object_type || 'media' === $object_type  || 'all' === $object_type ) {
		// Handle *_post_meta
		if ( $metadata_integration && apply_filters( 'pods_meta_handler', true, 'post' ) ) {
			if ( apply_filters( 'pods_meta_handler_get', true, 'post' ) ) {
				$hooks['filter']['get_post_metadata'] = [ 'get_post_metadata', [ PodsInit::$meta, 'get_post_meta' ], 10, 4 ];
			}

			if ( ! $is_tableless ) {
				$hooks['filter']['add_post_metadata']          = [ 'add_post_metadata', [ PodsInit::$meta, 'add_post_meta' ], 10, 5 ];
				$hooks['filter']['update_post_metadata']       = [ 'update_post_metadata', [ PodsInit::$meta, 'update_post_meta' ], 10, 5 ];
				$hooks['filter']['update_post_metadata_by_id'] = [ 'update_post_metadata_by_id', [ PodsInit::$meta, 'update_post_meta_by_id' ], 10, 4 ];
				$hooks['filter']['delete_post_metadata']       = [ 'delete_post_metadata', [ PodsInit::$meta, 'delete_post_meta' ], 10, 5 ];
				$hooks['filter']['delete_post_metadata_by_id'] = [ 'delete_post_metadata_by_id', [ PodsInit::$meta, 'delete_post_meta_by_id' ], 10, 2 ];
			}
		}

		// Add meta box groups.
		$hooks['action'][] = [ 'add_meta_boxes', [ PodsInit::$meta, 'meta_post_add' ], 10, 1 ];

		// Other post-related hooks.
		$hooks['filter'][] = [ 'enter_title_here', [ PodsInit::$meta, 'meta_post_enter_title_here' ], 10, 2 ];
		$hooks['filter'][] = [ 'wp_revisions_to_keep', [ PodsInit::$meta, 'meta_post_revisions_to_keep' ], 10, 2 ];

		if ( 'post' === $object_type || 'all' === $object_type ) {
			// Handle detecting new post.
			$hooks['action'][] = [ 'transition_post_status', [ PodsInit::$meta, 'save_post_detect_new' ], 10, 3 ];

			// Handle save.
			$hooks['action'][] = [ 'save_post', [ PodsInit::$meta, 'save_post' ], 10, 3 ];

			// Handle delete from relationships.
			$hooks['action'][] = [ 'delete_post', [ PodsInit::$meta, 'delete_post' ], 10, 1 ];

			if ( $watch_changed_fields ) {
				// Track changed fields.
				$hooks['action'][] = [
					'wp_insert_post_data',
					[ PodsInit::$meta, 'save_post_track_changed_fields' ],
					10,
					2,
				];
			}
		}
	}

	if ( ! $is_types_only && ( 'taxonomy' === $object_type || 'all' === $object_type ) ) {
		// Handle *_term_meta
		if ( $metadata_integration && apply_filters( 'pods_meta_handler', true, 'term' ) ) {
			if ( apply_filters( 'pods_meta_handler_get', true, 'term' ) ) {
				$hooks['filter'][] = [ 'get_term_metadata', [ PodsInit::$meta, 'get_term_meta' ], 10, 4 ];
			}

			if ( ! $is_tableless ) {
				$hooks['filter']['add_term_metadata']          = [ 'add_term_metadata', [ PodsInit::$meta, 'add_term_meta' ], 10, 5 ];
				$hooks['filter']['update_term_metadata']       = [ 'update_term_metadata', [ PodsInit::$meta, 'update_term_meta' ], 10, 5 ];
				$hooks['filter']['update_term_metadata_by_id'] = [ 'update_term_metadata_by_id', [ PodsInit::$meta, 'update_term_meta_by_id' ], 10, 4 ];
				$hooks['filter']['delete_term_metadata']       = [ 'delete_term_metadata', [ PodsInit::$meta, 'delete_term_meta' ], 10, 5 ];
				$hooks['filter']['delete_term_metadata_by_id'] = [ 'delete_term_metadata_by_id', [ PodsInit::$meta, 'delete_term_meta_by_id' ], 10, 2 ];
			}
		}

		// Handle save.
		$hooks['action'][] = [ 'edited_term', [ PodsInit::$meta, 'save_taxonomy' ], 10, 3 ];
		$hooks['action'][] = [ 'create_term', [ PodsInit::$meta, 'save_taxonomy' ], 10, 3 ];

		// Handle delete from relationships.
		$hooks['action'][] = [ 'delete_term_taxonomy', [ PodsInit::$meta, 'delete_taxonomy' ], 10, 1 ];

		// Handle form fields specific to the taxonomy.
		if ( $object ) {
			$hooks['action'][] = [ $object . '_edit_form_fields', [ PodsInit::$meta, 'meta_taxonomy' ], 10, 2 ];
			$hooks['action'][] = [ $object . '_add_form_fields', [ PodsInit::$meta, 'meta_taxonomy' ], 10, 1 ];
		}

		if ( $watch_changed_fields ) {
			// Track changed fields.
			$hooks['action'][] = [
				'edit_terms',
				[ PodsInit::$meta, 'save_taxonomy_track_changed_fields' ],
				10,
				2,
			];
		}

		/**
		 * Fires after a previously shared taxonomy term is split into two separate terms.
		 *
		 * @since 4.2.0
		 *
		 * @param int    $term_id          ID of the formerly shared term.
		 * @param int    $new_term_id      ID of the new term created for the $term_taxonomy_id.
		 * @param int    $term_taxonomy_id ID for the term_taxonomy row affected by the split.
		 * @param string $taxonomy         Taxonomy for the split term.
		 */
		$hooks['action'][] = [ 'split_shared_term', [ PodsInit::$meta, 'split_shared_term' ], 10, 4 ];
	}

	if ( ! $is_types_only && ( 'media' === $object_type || 'all' === $object_type ) ) {
		// Handle old AJAX attachment saving.
		$hooks['action'][] = [ 'wp_ajax_save-attachment-compat', [ PodsInit::$meta, 'save_media_ajax' ], 0, 1 ];

		// Maybe integrate with the media modal.
		if ( $media_modal_fields ) {
			// Handle showing meta fields in modal.
			$hooks['filter'][] = [ 'attachment_fields_to_edit', [ PodsInit::$meta, 'meta_media' ], 10, 2 ];
		}

		// Handle saving meta fields from modal.
		$hooks['filter'][] = [ 'attachment_fields_to_save', [ PodsInit::$meta, 'save_media' ], 10, 2 ];

		// Handle saving attachment metadata.
		$hooks['filter'][] = [ 'wp_update_attachment_metadata', [ PodsInit::$meta, 'save_media' ], 10, 2 ];

		// Handle delete.
		$hooks['action'][] = [ 'delete_attachment', [ PodsInit::$meta, 'delete_media' ], 10, 1 ];

		if ( $watch_changed_fields ) {
			// Track changed fields.
			$hooks['filter'][] = [
				'wp_insert_attachment_data',
				[ PodsInit::$meta, 'save_post_track_changed_fields' ],
				10,
				2,
			];
		}
	}

	if ( ! $is_types_only && ( 'user' === $object_type || 'all' === $object_type ) ) {
		// Handle *_user_meta.
		if ( $metadata_integration && apply_filters( 'pods_meta_handler', true, 'user' ) ) {
			if ( apply_filters( 'pods_meta_handler_get', true, 'user' ) ) {
				$hooks['filter'][] = [ 'get_user_metadata', [ PodsInit::$meta, 'get_user_meta' ], 10, 4 ];
			}

			if ( ! $is_tableless ) {
				$hooks['filter']['add_user_metadata']          = [ 'add_user_metadata', [ PodsInit::$meta, 'add_user_meta' ], 10, 5 ];
				$hooks['filter']['update_user_metadata']       = [ 'update_user_metadata', [ PodsInit::$meta, 'update_user_meta' ], 10, 5 ];
				$hooks['filter']['update_user_metadata_by_id'] = [ 'update_user_metadata_by_id', [ PodsInit::$meta, 'update_user_meta_by_id' ], 10, 4 ];
				$hooks['filter']['delete_user_metadata']       = [ 'delete_user_metadata', [ PodsInit::$meta, 'delete_user_meta' ], 10, 5 ];
				$hooks['filter']['delete_user_metadata_by_id'] = [ 'delete_user_metadata_by_id', [ PodsInit::$meta, 'delete_user_meta_by_id' ], 10, 2 ];
			}
		}

		// Handle showing fields in form.
		$hooks['action'][] = [ 'show_user_profile', [ PodsInit::$meta, 'meta_user' ], 10, 1 ];
		$hooks['action'][] = [ 'edit_user_profile', [ PodsInit::$meta, 'meta_user' ], 10, 1 ];

		// Handle saving from registration form.
		$hooks['action'][] = [ 'user_register', [ PodsInit::$meta, 'save_user' ], 10, 1 ];

		// Handle saving from profile update.
		$hooks['action'][] = [ 'profile_update', [ PodsInit::$meta, 'save_user' ], 10, 2 ];

		if ( $watch_changed_fields ) {
			// Track changed fields.
			$hooks['filter'][] = [
				'pre_user_login',
				[ PodsInit::$meta, 'save_user_track_changed_fields' ],
				10,
				1,
			];
		}
	}

	if ( ! $is_types_only && ( 'comment' === $object_type || 'all' === $object_type ) ) {
		if ( $metadata_integration && apply_filters( 'pods_meta_handler', true, 'comment' ) ) {
			// Handle *_comment_meta
			if ( apply_filters( 'pods_meta_handler_get', true, 'comment' ) ) {
				$hooks['filter'][] = [ 'get_comment_metadata', [ PodsInit::$meta, 'get_comment_meta' ], 10, 4 ];
			}

			if ( ! $is_tableless ) {
				$hooks['filter']['add_comment_metadata']           = [ 'add_comment_metadata', [ PodsInit::$meta, 'add_comment_meta' ], 10, 5 ];
				$hooks['filter']['update_comment_metadata']        = [ 'update_comment_metadata', [ PodsInit::$meta, 'update_comment_meta' ], 10, 5 ];
				$hooks['filter']['update_comment_metadata_by_id']  = [ 'update_comment_metadata_by_id', [ PodsInit::$meta, 'update_comment_meta_by_id' ], 10, 4 ];
				$hooks['filter']['delete_comment_metadata']        = [ 'delete_comment_metadata', [ PodsInit::$meta, 'delete_comment_meta' ], 10, 5 ];
				$hooks['filter']['delete_comment_metadata_by_id']  = [ 'delete_comment_metadata_by_id', [ PodsInit::$meta, 'delete_comment_meta_by_id' ], 10, 2 ];
			}
		}

		// Handle showing fields in form.
		$hooks['filter'][] = [ 'comment_form_submit_field', [ PodsInit::$meta, 'meta_comment_new' ], 10, 1 ];

		// Add meta box groups.
		$hooks['action'][] = [ 'add_meta_boxes_comment', [ PodsInit::$meta, 'meta_comment_add' ], 10, 1 ];

		// Handle validation for fields.
		$hooks['filter'][] = [ 'pre_comment_approved', [ PodsInit::$meta, 'validate_comment' ], 10, 2 ];

		// Handle saving comment from frontend.
		$hooks['action'][] = [ 'comment_post', [ PodsInit::$meta, 'save_comment' ], 10, 1 ];

		// Handle saving comment from admin.
		$hooks['action'][] = [ 'edit_comment', [ PodsInit::$meta, 'save_comment' ], 10, 1 ];

		if ( $watch_changed_fields ) {
			// Track changed fields.
			$hooks['action'][] = [
				'wp_update_comment_data',
				[ PodsInit::$meta, 'save_comment_track_changed_fields' ],
				10,
				3,
			];
		}
	}

	if ( ! $is_types_only && ( 'settings' === $object_type || 'all' === $object_type ) ) {
		// @todo Patch core to provide $option back in filters, patch core to add filter pre_add_option to add_option.

		// Undesirable way to do things which is heavy and requires access to looping through all fields, pulled from PodsMeta::core().
		/*foreach ( self::$settings as $setting_pod ) {
			foreach ( $setting_pod[ 'fields' ] as $option ) {
				add_filter( 'pre_option_' . $setting_pod[ 'name' ] . '_' . $option[ 'name' ], array( PodsInit::$meta, 'get_option' ), 10, 1 );
				add_action( 'add_option_' . $setting_pod[ 'name' ] . '_' . $option[ 'name' ], array( PodsInit::$meta, 'add_option' ), 10, 2 );
				add_filter( 'pre_update_option_' . $setting_pod[ 'name' ] . '_' . $option[ 'name' ], array( PodsInit::$meta, 'update_option' ), 10, 2 );
			}
		}*/
	}

	/**
	 * Allow filtering the list of actions and filters for a specific object type.
	 *
	 * @since 2.8.11
	 *
	 * @param array       $hooks       List of filters and actions for a specific object type.
	 * @param string      $object_type The object type.
	 * @param string|null $object      The object name.
	 */
	return (array) apply_filters( 'pods_meta_hook_list', $hooks, $object_type, $object );
}

/**
 * Turn off conflicting / recursive actions for an object type that Pods hooks into
 *
 * @since 2.0.0
 *
 * @param string      $object_type The object type.
 * @param string|null $object      The object name.
 *
 * @return bool Whether no conflict mode was turned on.
 */
function pods_no_conflict_on( $object_type = 'post', $object = null ) {
	if ( 'post_type' === $object_type ) {
		$object_type = 'post';
	} elseif ( 'term' === $object_type ) {
		$object_type = 'taxonomy';
	}

	if ( ! class_exists( 'PodsInit' ) ) {
		pods_init();
	}

	if ( ! empty( PodsInit::$no_conflict[ $object_type ] ) ) {
		return true;
	}

	if ( ! is_object( PodsInit::$meta ) ) {
		return false;
	}

	$no_conflict = pods_meta_hook_list( $object_type );

	$conflicted = false;

	foreach ( $no_conflict as $action_filter => $conflicts ) {
		foreach ( $conflicts as $k => $args ) {
			if ( call_user_func_array( 'has_' . $action_filter, array_slice( $args, 0, 2 ) ) ) {
				call_user_func_array( 'remove_' . $action_filter, array_slice( $args, 0, 3 ) );

				$conflicted = true;
			} else {
				unset( $no_conflict[ $action_filter ][ $k ] );
			}
		}
	}

	if ( $conflicted ) {
		PodsInit::$no_conflict[ $object_type ] = $no_conflict;

		return true;
	}

	return false;
}

/**
 * Turn on actions after running code during pods_conflict
 *
 * @since 2.0.0
 *
 * @param string      $object_type The object type.
 * @param string|null $object      The object name.
 * @param bool        $force       Whether to force turning all hooks back on even if they were already off.
 *
 * @return bool Whether no conflict mode was on and was successfully turned off.
 */
function pods_no_conflict_off( $object_type = 'post', $object = null, $force = false ) {
	if ( 'post_type' === $object_type ) {
		$object_type = 'post';
	} elseif ( 'term' === $object_type ) {
		$object_type = 'taxonomy';
	}

	if ( ! class_exists( 'PodsInit' ) ) {
		pods_init();
	}

	if ( ! is_object( PodsInit::$meta ) ) {
		return false;
	}

	if ( $force ) {
		// Turn ALL hooks back on.
		$no_conflict = pods_meta_hook_list( $object_type, $object );
	} else {
		// No conflict mode was not already on.
		if ( empty( PodsInit::$no_conflict[ $object_type ] ) ) {
			return false;
		}

		// Only turn on the hooks we turned off for no conflict mode.
		$no_conflict = PodsInit::$no_conflict[ $object_type ];
	}

	$conflicted = false;

	foreach ( $no_conflict as $action_filter => $conflicts ) {
		foreach ( $conflicts as $args ) {
			if ( ! call_user_func_array( 'has_' . $action_filter, array_slice( $args, 0, 2 ) ) ) {
				call_user_func_array( 'add_' . $action_filter, $args );

				$conflicted = true;
			}
		}
	}

	if ( isset( PodsInit::$no_conflict['all'] ) ) {
		unset( PodsInit::$no_conflict['all'] );
	}

	if ( $conflicted ) {
		unset( PodsInit::$no_conflict[ $object_type ] );

		return true;
	}

	return false;
}

/**
 * Returns a list of all WordPress and Pods reserved keywords.
 *
 * @link https://codex.wordpress.org/Reserved_Terms
 *
 * @since 2.7.15
 *
 * @param null|string $context The reserved keyword context.
 *
 * @return array
 */
function pods_reserved_keywords( $context = null ) {
	// WordPress Post keywords.
	$wp_post_keywords = [
		'id',
		'ID',
		'attachment',
		'attachment_id',
		'author',
		'author_name',
		'category',
		'link_category',
		'name',
		'p',
		'page',
		'paged',
		'post',
		'post_format',
		'post_mime_type',
		'post_status',
		'post_tag',
		'post_thumbnail',
		'post_thumbnail_url',
		'post_type',
		's',
		'search',
		'tag',
		'taxonomy',
		'term',
		'terms',
		'title',
		'type',
	];

	// WordPress keywords.
	$wp_keywords = [
		'calendar',
		'cat',
		'category__and',
		'category__in',
		'category__not_in',
		'category_name',
		'comments_per_page',
		'comments_popup',
		'cpage',
		'custom',
		'customize_messenger_channel',
		'customized',
		'day',
		'debug',
		'embed',
		'error',
		'exact',
		'feed',
		'hour',
		'm',
		'minute',
		'monthnum',
		'more',
		'nav_menu',
		'nonce',
		'nopaging',
		'offset',
		'order',
		'orderby',
		'page_id',
		'pagename',
		'pb',
		'perm',
		'post__in',
		'post__not_in',
		'posts',
		'posts_per_archive_page',
		'posts_per_page',
		'preview',
		'robots',
		'second',
		'sentence',
		'showposts',
		'static',
		'subpost',
		'subpost_id',
		'tag__and',
		'tag__in',
		'tag__not_in',
		'tag_id',
		'tag_slug__and',
		'tag_slug__in',
		'tb',
		'theme',
		'w',
		'withcomments',
		'withoutcomments',
		'year',
	];

	// Pods keywords.
	$pods_keywords = [
		'id',
		'ID',
	];

	$keywords = [];

	// Add keywords for WP context.
	if ( in_array( $context, [ null, 'wp' ], true ) ) {
		$keywords = array_merge( $keywords, $wp_keywords );
	}

	// Add keywords for WP Post contexts (also applies to WP context).
	if ( in_array( $context, [ null, 'wp', 'wp-post' ], true ) ) {
		$keywords = array_merge( $keywords, $wp_post_keywords );
	}

	// Add keywords for Pods context.
	if ( in_array( $context, [ null, 'pods' ], true ) ) {
		$keywords = array_merge( $keywords, $pods_keywords );
	}

	$keywords = array_filter( array_unique( $keywords ) );

	/**
	 * Filter the WordPress and Pods reserved keywords.
	 *
	 * @since 2.7.15
	 *
	 * @param array       $keywords List of WordPress and Pods reserved keywords.
	 * @param null|string $context  The reserved keyword context.
	 */
	return apply_filters( 'pods_reserved_keywords', $keywords, $context );
}

/**
 * Get the value for a Pods setting.
 *
 * @since 2.8.0
 *
 * @param string $setting_name The setting name.
 * @param null   $default      The default value if the setting is not yet set.
 *
 * @return mixed The setting value.
 */
function pods_get_setting( $setting_name, $default = null ) {
	$settings = pods_container( Settings::class );

	// Fallback for the setting handling if it's too early.
	if ( ! $settings ) {
		$settings = get_option( Settings::OPTION_NAME, [] );

		$setting = pods_v( $setting_name, (array) $settings, $default );

		if ( null !== $default && ( null === $setting || '' === $setting ) ) {
			return $default;
		}

		return $setting;
	}

	return $settings->get_setting( $setting_name, $default );
}

/**
 * Get the Pods settings.
 *
 * @since 2.8.0
 *
 * @return array The setting values.
 */
function pods_get_settings() {
	$settings = pods_container( Settings::class );

	return $settings->get_settings();
}

/**
 * Update the value for a Pods setting.
 *
 * @since 2.8.0
 *
 * @param string $setting_name  The setting name.
 * @param mixed  $setting_value The setting value.
 */
function pods_update_setting( $setting_name, $setting_value ) {
	$settings = pods_container( Settings::class );

	$settings->update_setting( $setting_name, $setting_value );
}

/**
 * Update the settings for a Pods.
 *
 * @since 2.8.0
 *
 * @param array $setting_values The list of settings to update, pass null as a value to remove it.
 */
function pods_update_settings( $setting_values ) {
	$settings = pods_container( Settings::class );

	$settings->update_settings( $setting_values );
}

/**
 * Determine if Session Auto Start is enabled.
 *
 * @param bool $check_constant_only Whether to only check the constant.
 *
 * @since 2.8.0
 *
 * @return bool|string|null Boolean if it is set to on or off, "auto" if set to auto, and null if $check_constant_only and constant is not set.
 */
function pods_session_auto_start( $check_constant_only = false ) {
	if ( defined( 'PODS_SESSION_AUTO_START' ) ) {
		return PODS_SESSION_AUTO_START;
	}

	if ( $check_constant_only ) {
		return null;
	}

	$auto_start = pods_get_setting( 'session_auto_start', 'auto' );

	// Check for "auto" string and return that.
	if ( 'auto' === $auto_start ) {
		return $auto_start;
	}

	return FILTER_VAR( $auto_start, FILTER_VALIDATE_BOOLEAN );
}

/**
 * Determine if we can use PHP sessions.
 *
 * @since 2.8.0
 *
 * @param bool $only_env_check Whether to ignore constant/option/logged in checks.
 *
 * @return bool Whether we can use sessions.
 */
function pods_can_use_sessions( $only_env_check = false ) {
	// Maybe check non-environment factors.
	if ( ! $only_env_check ) {
		// We do not need a session ID if there is a valid user logged in.
		if ( is_user_logged_in() ) {
			return false;
		}

		// Allow for bypassing Pods session auto-starting.
		if ( ! pods_session_auto_start() ) {
			return false;
		}
	}

	if ( ! defined( 'PANTHEON_SESSIONS_ENABLED' ) || ! PANTHEON_SESSIONS_ENABLED ) {
		// We aren't using Pantheon WP Native Sessions plugin so let's check if the normal session will work.

		$save_path = session_save_path();

		if ( ! function_exists( 'session_status' ) || PHP_SESSION_DISABLED === session_status() ) {
			// Sessions are disabled.
			return false;
		}

		if ( 0 === strpos( $save_path, 'tcp://' ) ) {
			// Allow for non-file based sessions, like Memcache.
			// This is OK, but we don't want to check if file_exists on next statement.
		} elseif ( empty( $save_path ) || ! @file_exists( $save_path ) || ! is_writable( $save_path ) ) {
			// Check if session path exists and can be written to, avoiding PHP fatal errors.
			return false;
		}
	}

	// Allow sessions.
	return true;
}

/**
 * Safely start a new session (without white screening on certain hosts,
 * which have no session path or the path is not writable).
 *
 * @since 2.3.10
 *
 * @return boolean Whether the session was started.
 */
function pods_session_start() {
	if ( function_exists( 'session_status' ) && PHP_SESSION_ACTIVE === session_status() ) {
		return true;
	}

	if ( false !== headers_sent() ) {
		// Check if headers were sent.
		return false;
	}

	if ( ! pods_can_use_sessions() ) {
		return false;
	}

	// Start session
	return @session_start();
}

/**
 * Get current session ID.
 *
 * @since 2.7.23
 *
 * @return string The session ID.
 */
function pods_session_id() {
	if ( false === pods_session_start() ) {
		$session_id = '';
	} else {
		$session_id = @session_id();
	}

	/**
	 * Allow overriding the session ID used by Pods.
	 *
	 * @since 3.1.0
	 *
	 * @param string $session_id The session ID.
	 */
	return (string) apply_filters( 'pods_session_id', $session_id );
}

/**
 * @todo  : replace string literal with a defined constant
 *
 * @return bool
 *
 * @since 2.7.0
 */
function pods_is_modal_window() {
	$is_modal_window = false;

	if ( ! empty( $_GET['pods_modal'] ) || ! empty( $_POST['pods_modal'] ) ) {
		$is_modal_window = true;
	}

	return $is_modal_window;
}

/**
 * Check if the Pods object is exists and is valid.
 *
 * @param Pods|mixed $pod The Pods object or something that isn't a pod object.
 *
 * @return bool Whether the Pods object is exists and is valid.
 *
 * @since 2.7.0
 */
function pod_is_valid( $pod ) {
	return $pod instanceof Pods && $pod->valid();
}

/**
 * Check if the pod object has item(s).
 *
 * @param Pods|mixed $pod The pod object or something that isn't a pod object
 *
 * @return bool Whether the pod object has items
 *
 * @since 2.7.0
 */
function pod_has_items( $pod ) {
	if ( ! pod_is_valid( $pod ) ) {
		return false;
	}

	if (
		(
			$pod->id
			&& $pod->exists()
		)
		|| (
			! empty( $pod->params )
			&& 0 < $pod->total()
		)
	) {
		return true;
	}

	return false;
}

/**
 * Merge one config into another for purposes of overriding certain arguments.
 *
 * @since 2.8.0
 *
 * @param array|Field $config_to_merge_into The config to merge into.
 * @param array|Field $config_to_merge_from The config to merge from.
 *
 * @return array|Field|Value_Field The final config result.
 */
function pods_config_merge_data( $config_to_merge_into, $config_to_merge_from ) {
	// The configs already match.
	if ( $config_to_merge_into === $config_to_merge_from ) {
		return $config_to_merge_into;
	}

	// Merge the config into the destination config if both are Value_Field.
	if ( $config_to_merge_into instanceof Value_Field && $config_to_merge_from instanceof Value_Field ) {
		$field_object_into = $config_to_merge_into->get_field_object();
		$field_value_into  = $config_to_merge_into->get_field_value();

		$field_object_from = $config_to_merge_from->get_field_object();
		$field_value_from  = $config_to_merge_from->get_field_value();

		$config_to_merge_into->set_field_object( pods_config_merge_data( $field_object_into, $field_object_from ) );
		$config_to_merge_into->set_field_value( $field_value_from );

		return $config_to_merge_into;
	}

	// Merge the config into the destination config.
	if ( $config_to_merge_into instanceof Whatsit ) {
		return $config_to_merge_into->set_args( $config_to_merge_from );
	}

	// Merge the destination config into the config but don't replace data.
	if ( $config_to_merge_from instanceof Whatsit ) {
		return $config_to_merge_from->set_args( $config_to_merge_into, false );
	}

	// The config was not an array.
	if ( ! is_array( $config_to_merge_from ) ) {
		return $config_to_merge_into;
	}

	// The config was not an array.
	if ( ! is_array( $config_to_merge_into ) ) {
		return $config_to_merge_from;
	}

	// Merge the config arrays together.
	return array_merge( $config_to_merge_into, $config_to_merge_from );
}

/**
 * Merge multiple configs into others for purposes of overriding certain arguments.
 *
 * @since 2.8.0
 *
 * @param array[]|Field[] $configs_to_merge_into The configs to merge into.
 * @param array[]|Field[] $configs_to_merge_from The configs to merge from.
 *
 * @return array[]|Field[] The final config results.
 */
function pods_config_merge_fields( $configs_to_merge_into, $configs_to_merge_from ) {
	// The configs already match.
	if ( $configs_to_merge_into === $configs_to_merge_from ) {
		return $configs_to_merge_into;
	}

	foreach ( $configs_to_merge_from as $key => $config_to_merge_from ) {
		if ( ! isset( $config_to_merge_into[ $key ] ) ) {
			$configs_to_merge_into[ $key ] = $config_to_merge_from;

			continue;
		}

		$configs_to_merge_into[ $key ] = pods_config_merge_data( $config_to_merge_into[ $key ], $config_to_merge_from );
	}

	return $configs_to_merge_into;
}

/**
 * Get the list of all fields, including object fields, from a Pod configuration.
 *
 * @since 2.8.0
 *
 * @param array|Pod|Pods $pod The Pod configuration array or object.
 *
 * @return array[]|Field[] The list of all fields, including object fields.
 */
function pods_config_get_all_fields( $pod ) {
	$pod = pods_config_for_pod( $pod );

	if ( ! $pod ) {
		return [];
	}

	return $pod->get_all_fields();
}

/**
 * Get the field object from a value field object.
 *
 * @since 2.8.0
 *
 * @param Value_Field|Field $value_field The value field object.
 *
 * @return Field The field object.
 */
function pods_config_get_field_from_value_field( $value_field ) {
	// Maybe get the field object.
	if ( $value_field instanceof Value_Field ) {
		return $value_field->get_field_object();
	}

	return $value_field;
}

/**
 * Get the list of all field objects from a list of value field objects.
 *
 * @since 2.8.0
 *
 * @param Value_Field[]|Field[] $value_fields The list of value field objects.
 *
 * @return Field[] The list of all field objects.
 */
function pods_config_get_fields_from_value_fields( array $value_fields ) {
	$all_fields = [];

	foreach ( $value_fields as $key => $field ) {
		// Maybe get the field object.
		if ( $field instanceof Value_Field ) {
			$field = $field->get_field_object();
		}

		if ( is_int( $key ) ) {
			$all_fields[] = $field;
		} else {
			$all_fields[ $key ] = $field;
		}
	}

	return $all_fields;
}

/**
 * Get the field data for a specific field matching from all fields, including object fields, from a Pod configuration.
 *
 * @since 2.8.0
 *
 * @param string         $field The field name to get.
 * @param array|Pod|Pods $pod   The Pod configuration array or object.
 * @param null|string    $arg   The field argument to use when getting the field.
 *
 * @return array|Field|null The field data or null if not found.
 */
function pods_config_get_field_from_all_fields( $field, $pod, $arg = null ) {
	$pod = pods_config_for_pod( $pod );

	// The pod isn't there or valid.
	if ( empty( $pod ) ) {
		return null;
	}

	return $pod->get_field( $field );
}

/**
 * Get a normalized Pod configuration.
 *
 * @since 2.8.0
 *
 * @param Pod|Pods|array|string $pod The Pod configuration object, Pods() object, old-style array, or name.
 *
 * @return false|Pod The Pod object or false if invalid.
 */
function pods_config_for_pod( $pod ) {
	if ( $pod instanceof Pod ) {
		return $pod;
	}

	if ( $pod instanceof Pods ) {
		// Check if the $pod is invalid.
		if ( ! $pod->valid() ) {
			return false;
		}

		return $pod->pod_data;
	}

	if ( is_string( $pod ) ) {
		try {
			$api = pods_api();

			$pod = $api->load_pod( [ 'name' => $pod ] );
		} catch ( Exception $exception ) {
			return false;
		}

		// Check if the $pod is invalid.
		if ( ! $pod ) {
			return false;
		}

		return $pod;
	}

	if ( ! is_array( $pod ) ) {
		return false;
	}

	$pod = new Pod( $pod );

	if ( ! $pod->is_valid() ) {
		return false;
	}

	return $pod;
}

/**
 * Get a normalized Field configuration.
 *
 * @since 2.9.8
 *
 * @param Field|array|string    $field The Field configuration object, Pods() object, old-style array, or name.
 * @param Pod|Pods|array|string $pod   The Pod configuration object, Pods() object, old-style array, or name.
 *
 * @return false|Field The Field object or false if invalid.
 */
function pods_config_for_field( $field, $pod = null ) {
	if ( $field instanceof Field ) {
		return $field;
	}

	if ( $pod ) {
		$pod = pods_config_for_pod( $pod );

		if ( ! $pod ) {
			$pod = null;
		}
	}

	if ( $pod && is_string( $field ) ) {
		$field = $pod->get_field( $field );

		// Check if the $field is invalid.
		if ( ! $field ) {
			return false;
		}

		return $field;
	}

	if ( ! is_array( $field ) ) {
		return false;
	}

	$field = new Field( $field );

	if ( ! $field->is_valid() ) {
		return false;
	}

	return $field;
}

function is_pods_alternative_cache_activated() {
	return function_exists( 'pods_alternative_cache_init' );
}

function is_pods_alternative_cache_activated_test() {
	$result = [
		'label'       => __( 'You are using the Pods Team recommended Pods Alternative Cache plugin', 'pods' ),
		'status'      => 'good',
		'badge'       => [
			'label' => __( 'Security' ),
			'color' => 'blue',
		],
		'description' => sprintf( '<p>%s</p>', __( 'The Pods Alternative Cache plugin is useful to many who use Pods.' ) ),
		'actions'     => sprintf( '<p><a href="%s">%s</a></p>', esc_url( admin_url( 'plugins.php' ) ), __( 'Manage your plugins' ) ),
		'test'        => 'is_pods_alternative_cache_activated',
	];

	return $result;
}

/**
 * Get the SVG icon data (base64 or svg itself) for the icon or the dashicon default class.
 *
 * @since 2.8.1
 *
 * @param string $icon_path The icon name or the SVG full file path to use.
 * @param string $default   The dashicons helper class (dashicons-database) to use if SVG not found.
 * @param string $mode      How to return the SVG (base64 or svg).
 *
 * @return string The SVG icon data (base64 or svg itself) for the icon or the dashicon default class.
 */
function pods_svg_icon( $icon_path, $default = 'dashicons-database', $mode = 'base64' ) {
	// Return the default when doing WP-CLI requests and Codeception testing.
	if ( ( defined( 'WP_CLI' ) && WP_CLI ) || function_exists( 'codecept_debug' ) ) {
		return $default;
	}

	if ( 'pods' === $icon_path ) {
		$icon_path = PODS_DIR . '/ui/images/icon-menu.svg';
	}

	$icon = pods_static_cache_get( $icon_path, __FUNCTION__ . '/' . $mode );

	// If the cached icon did not exist, use default.
	if ( '404-not-exists' === $icon ) {
		return $default;
	}

	// If the cached icon was found and is not empty, return it.
	if ( is_string( $icon ) && '' !== $icon ) {
		return $icon;
	}

	/**
	 * Allow filtering the SVG icon used and bypass the normal functionality.
	 *
	 * @since 2.8.2
	 *
	 * @param null|string $icon      The icon to use.
	 * @param string      $icon_path The SVG full file path to use.
	 * @param string      $default   The dashicons helper class (dashicons-database) to use if SVG not found.
	 * @param string      $mode      How to return the SVG (base64 or svg).
	 */
	$icon = apply_filters( 'pods_svg_icon', null, $icon_path, $default, $mode );

	if ( null !== $icon ) {
		return $icon;
	}

	if ( ! file_exists( $icon_path ) ) {
		pods_static_cache_set( '404-not-exists', $icon, __FUNCTION__ . '/' . $mode );

		return $default;
	}

	$svg_data = file_get_contents( $icon_path );

	if ( ! $svg_data ) {
		pods_static_cache_set( '404-not-exists', $icon, __FUNCTION__ . '/' . $mode );

		return $default;
	}

	pods_static_cache_set( $icon_path, $icon, __FUNCTION__ . '/' . $mode );

	// If mode is SVG data, return that.
	if ( 'svg' === $mode ) {
		return $svg_data;
	}

	// Default mode is base64.
	return 'data:image/svg+xml;base64,' . base64_encode( $svg_data );
}

/**
 * Determine whether Pods is being used for content types only.
 *
 * @since 2.8.11
 *
 * @param bool        $check_constant_only Whether to only check the constant, unless there's a filter overriding things.
 * @param null|string $content_type        The content type context we are in. If this is a _pods_* content type, this will enable types for internal purposes.
 *
 * @return bool Whether Pods is being used for content types only.
 */
function pods_is_types_only( $check_constant_only = false, $content_type = null ) {
	if (
		$content_type
		&& (
			0 === strpos( $content_type, '_pods_' )
			|| 0 === strpos( $content_type, 'pod/_pods_' )
		)
	) {
		return false;
	}

	// Check if Pods is only being used for content types only.
	if ( defined( 'PODS_META_TYPES_ONLY' ) ) {
		return filter_var( PODS_META_TYPES_ONLY, FILTER_VALIDATE_BOOLEAN );
	}

	// Return null we want to only check the constant, unless there's a filter overriding things.
	if ( $check_constant_only && ! has_filter( 'pods_is_types_only' ) ) {
		return null;
	}

	$is_types_only = pods_get_setting( 'types_only', '0' );
	$is_types_only = filter_var( $is_types_only, FILTER_VALIDATE_BOOLEAN );

	/**
	 * Allow filtering whether Pods is being used for content types only.
	 *
	 * @since 2.8.11
	 *
	 * @param bool        $is_types_only Whether Pods is being used for content types only.
	 * @param null|string $content_type The content type context we are in.
	 */
	return (bool) apply_filters( 'pods_is_types_only', $is_types_only, $content_type );
}

/**
 * Set up the container and return it.
 *
 * @since 2.8.17
 *
 * @param string|null $slug_or_class Either the slug of a binding previously registered using singleton or
 *                                   register or the full class name that should be automagically created or
 *                                   `null` to get the container instance itself.
 *
 * @return mixed|null The pods_container() object or null if the function does not exist yet.
 */
function pods_container( $slug_or_class = null ) {
	$container = Container_DI52::init();

	return null === $slug_or_class ? $container : $container->make( $slug_or_class );
}

/**
 * Set up the container callback and return it.
 *
 * @since 2.9.18
 *
 * @param  string $slug_or_class A class or interface fully qualified name or a string slug.
 * @param  string $method        The method that should be called on the resolved implementation with the
 *                               specified array arguments.
 * @param  mixed  [$argsN]       (optional) Any number of arguments that will be passed down to the Callback.
 *
 * @return callable|null A PHP Callable based on the Slug and Methods passed or null if the function does not exist yet.
 */
function pods_container_callback( $slug_or_class, $method ) {
	$container = Container_DI52::init();

	$arguments = func_get_args();
	$is_empty  = 2 === count( $arguments );

	if ( $is_empty ) {
		$callable = $container->callback( $slug_or_class, $method );
	} else {
		$callback = $container->callback( 'callback', 'get' );
		$callable = call_user_func_array( $callback, $arguments );
	}

	return $callable;
}

/**
 * Register the service provider.
 *
 * @since 2.9.18
 *
 * @param  string $provider_class The full class name for the service provider.
 */
function pods_container_register_service_provider( $provider_class ) {
	$container = Container_DI52::init();

	$container->register( $provider_class );
}

/**
 * Maybe log data to the Pods debug log.
 *
 * @since 3.2.7
 *
 * @param mixed  $debug    The debug data to track.
 * @param string $context  The context where the debug came from.
 * @param string $function The function/method name where the debug was called.
 * @param int    $line     The line number where the debug was called.
 *
 * @return void
 */
function pods_debug_log_data( $debug, string $context, string $function, int $line ): void {
	if ( ! pods_is_debug_logging_enabled() ) {
		return;
	}

	/**
	 * Log data to the Pods debug log.
	 *
	 * @since 3.2.7
	 *
	 * @param mixed  $debug    The debug data to track.
	 * @param string $context  The context where the debug came from.
	 * @param string $function The function/method name where the debug was called.
	 * @param int    $line     The line number where the debug was called.
	 */
	do_action( 'pods_debug_log_data', $debug, $context, $function, $line );
}

/**
 * Determine whether Pods debug logging is enabled.
 *
 * @since 3.2.7
 *
 * @return bool Whether Pods debug logging is enabled.
 */
function pods_is_debug_logging_enabled(): bool {
	if ( defined( 'PODS_DEBUG_LOGGING' ) ) {
		return (bool) PODS_DEBUG_LOGGING;
	}

	/**
	 * Allow filtering whether debug logging is enabled.
	 *
	 * @since 3.2.7
	 *
	 * @param bool $is_debug_logging_enabled Whether debug logging is enabled.
	 */
	return (bool) apply_filters( 'pods_is_debug_logging_enabled', pods_is_debug_display() );
}
