$value) { return $key; } return null; } } /** * Class Utilities * * @package AuRise\Plugin\Video */ class Utilities { /** * Array Key Exists and Has Value * * @since 1.0.0 * @param string|int $key The key to search for in the array. * @param array $array The array to search. * @param mixed $default The default value to return if not found or is empty. Default is an empty string. * @return mixed|null The value of the key found in the array if it exists or the value of `$default` if not found or is empty. */ public static function array_has_key($key, $array = array(), $default = '') { //Check if this key exists in the array $valid_key = (is_string($key) && !empty($key)) || is_numeric($key); $valid_array = is_array($array) && count($array); if ($valid_key && $valid_array && array_key_exists($key, $array)) { //Always return if it's a boolean or number, otherwise only return it if it has any value if ($array[$key] || is_bool($array[$key]) || is_numeric($array[$key])) { return $array[$key]; } } return $default; } /** * Retrieve the next timestamp for an event * * @since 2.0.0 * @param string $hook Action hook to execute when event is run * @param array|false $args Optional. Arguments to pass to the hook's callback function. If false, then it will look for the next upcoming event for this hook, regardless of arguments. * @param bool $return_all Optional. If true and the cron job was found, this function will return an associative array with keys for `hook`, `timestamp`, and `args` * @return int|array|false The Unix timestamp of the next time the scheduled event will occur or false if not found. */ public static function next_scheduled($hook, $args = false, $return_all = false) { if (is_array($args)) { return wp_next_scheduled($hook, $args); //Default WordPress functionality } //Not ideal... $crons = _get_cron_array(); if (is_array($crons)) { foreach ($crons as $timestamp => $cron) { if (self::array_has_key($hook, $cron)) { if ($return_all) { self::debug_log($cron); $key = array_key_first($cron['hook']); return array( 'hook' => $hook, 'timestamp' => $timestamp, 'args' => $cron[$hook][$key]['args'] ); } return $timestamp; } } } return false; } /** * Send Time to Server-Timing API * * @param DateTime $start a DateTime object of when the timing begain * @param string $name The name of what you're timing * @param string $key Optional. The key of what you're timing. Default is `wordpressPlugin` */ public static function server_timing($start, $name, $key = 'wordpressPlugin') { $end = new DateTime('now'); header(sprintf( 'Server-Timing: %s;desc="%s";dur=%s', $key, $name, intval($end->format('Uv')) - intval($start->format('Uv')) //Measured in milliseconds ), false); } /** * Check if Cache Should Be Cleared * * @param bool $logged_in Refresh cache if user is logged in, otherwise respond to queries only. * @return bool True if cache should be refreshed. False otherwise. */ public static function refresh_cache($logged_in = true) { global $wp_customize; $soft_check = isset($_GET['nocache']) || (isset($_GET['avoid-minify']) && $_GET['avoid-minify'] == 'true') || isset($wp_customize); if (!$soft_check && $logged_in) { return is_user_logged_in(); } return $soft_check; } /** * Get Caching Prefix * * Prefixes are unique to language, group, and post ID * * @param string $group Optional * @param int $post_id Optional * * @return string A sanitized cache key prefix */ private static function cache_prefix($group = '', $post_id = '') { $prefix = 'locale-' . get_user_locale(0); //Get locale based on user, pass 0 so it looks them up if ($group) { $prefix .= '_' . $group; } if ($post_id) { $prefix .= '_post-' . $post_id; } return sanitize_key($prefix); } /** * Cache Data * * @param string $key The key to use. This function will sanitize it for the database. Expected to not be SQL-escaped. Must be 172 characters or fewer in length. * @param mixed $value The data to be cached * @param int $expire Optional. The number of hours to cache before expiration. Default is 4 * @param string $type Optional. The type of caching method. Can be `transient`, `cache`, or `both`. Default is `both` * @param string $group Optional. The grouping for this cache. Default is an empty string. The value is always automatically prepended with language data. * @param int|false $post_id Optional. If this data is associted with a post, pass the post ID or false to save it to the post's meta data * @param bool $generated_at Optional. If you want to display a timestamp for when this item was cached, set to true. Default is false. $value must be a string. * * @return mixed $value */ public static function set_cache($key, $value, $expire = 4, $type = 'both', $group = '', $post_id = '', $generated_at = false) { if ($key) { $prefix = self::cache_prefix($group, $post_id); $key = sanitize_key($key); $transient_key = $prefix . '_' . $key; if ($generated_at && is_string($value)) { $value = sprintf( '', $transient_key, date('n/j/Y H:i:s:u T'), $expire ) . $value; } if ($type !== 'transient') { //Documentation: https://developer.wordpress.org/reference/functions/wp_cache_set/ wp_cache_set($key, $value, $prefix, $expire * HOUR_IN_SECONDS); } if ($type !== 'cache') { //Documentation: https://developer.wordpress.org/reference/functions/set_transient/ set_transient($transient_key, $value, $expire * HOUR_IN_SECONDS); } } return $value; } /** * Get Cached Data * * @param string $key The key to use. This function will sanitize it for the database * @param string $type Optional. The type of caching method. Can be `transient`, `cache`, or `both`. Default is `both` * @param string $group Optional. The grouping for this cache. Default is an empty string. The value is always automatically prepended with language data. * @param int $post_id Optional. If this data is associted with a post, pass the post ID or false to attempt retrieval from the post's meta data * * @return mixed|false Value of data, false on failure to retrieve contents. */ public static function get_cache($key, $type = 'both', $group = '', $post_id = '') { $value = false; if ($key) { $prefix = self::cache_prefix($group, $post_id); $key = sanitize_key($key); //If type is `cache` or `both, attempt to get the cache first if ($type !== 'transient' && $value === false) { //Documentation: https://developer.wordpress.org/reference/functions/wp_cache_get/ $value = wp_cache_get( $key, //(int|string) (Required) The key under which the cache contents are stored. $prefix //(string) (Optional) Where the cache contents are grouped. Default value: '' //false, //(bool) (Optional) Whether to force an update of the local cache from the persistent cache. Default value: false //null//(bool) (Optional) Whether the key was found in the cache (passed by reference). Disambiguates a return of false, a storable value. Default value: null ); //(mixed|false) The cache contents on success, false on failure to retrieve contents. } if ($type !== 'cache' && $value === false) { //Documentation: https://developer.wordpress.org/reference/functions/get_transient/ $value = get_transient($prefix . '_' . $key); //(mixed|false) Value of transient, false on failure to retrieve contents. } } return $value; } /** * Parse Content for Specified Shortcodes * * Parse a string of content for a specific shortcode to retrieve its attributes and content * * @param string $content The content to parse * @param string $tag The shortcode tag * @param bool $closing_tag If true, it will look for closing tags. If false, it assumes the shortcode is self-closing. Default is false. * @return array An associative array with `tag` (string) and `shortcodes` (sequential array). If shortcodes were discovered, each one has keys for `atts` (associative array) and `content` (string) */ public static function discover_shortcodes($content, $tag, $closing_tag = false) { $return = array( 'tag' => $tag, 'shortcodes' => array() ); $original_content = $content; //Store original content $normalized_content = self::normalize_html($content); //Remove all the junk $content = $normalized_content; //Search for shortcodes with attributes $start_tag = '[' . $tag . ' '; //Opens the start tag, assumes there are attributes because of the space $start_tag_end = ']'; //Closes the start tag if ($closing_tag) { //If this is NOT a self-closing tag, it will have a closing tag after content $closing_tag = '[/' . $tag . ']'; } $start = strpos($content, $start_tag); //Get the first position of the opening tag if ($start !== false) { while ($start !== false) { $shortcode = array( 'atts' => array(), 'content' => '' ); //Parse for shortcode attributes // [example att1="foo" att2="bar"]content[/example] //Find the closing start tag, offset by the end of this specific opening tag $start_tag_end_pos = strpos($content, $start_tag_end, $start); if ($start_tag_end_pos !== false) { $atts_str_len = ($start_tag_end_pos + strlen($start_tag_end)) - $start; //String length, including closing tag //Chop only the attributes e.g. `att1="foo" att2="bar"` $atts_str = trim(str_replace( array($start_tag, $start_tag_end), //Remove the tag strings '', //Replace with nothing substr($content, $start, $atts_str_len) )); if (strpos($atts_str, '"') !== false) { $atts = explode('" ', substr( $atts_str, 0, -1 //Clip off the last character, which is a double quote )); if (is_array($atts) && count($atts)) { foreach ($atts as $att_str) { $pair = explode('="', $att_str); if (is_array($pair) && count($pair) > 1) { //Validate & normalize the key $key = is_string($pair[0]) ? trim($pair[0]) : ''; if (!empty($key)) { $shortcode['atts'][$key] = is_string($pair[1]) ? html_entity_decode($pair[1]) : $pair[1]; } } } } } //Now check for content, starting where the start tag ended off $content_end = $start + $atts_str_len; //End after the self-closing start tag $closing_tag_pos = strpos($content, $closing_tag, $content_end); if ($closing_tag_pos !== false) { $content_length = $closing_tag_pos - $content_end; //Get the content between the opening and closing tags $shortcode['content'] = substr($content, $content_end, $content_length); $content_end += strlen($closing_tag); //End after the closing tag } //If anything was discovered in this shortcode, add it to the return object if (count($shortcode['atts']) || !empty($shortcode['content'])) { $return['shortcodes'][] = $shortcode; } $content = substr($content, $content_end); //Remove this shortcode from the content to continue parsing in the while-loop $start = strpos($content, $start_tag); //Get the position of the next opening tag } } } //Search for shortcodes without attributes $content = $normalized_content; //Reset the content back to it's original, normalized state $start_tag = '[' . $tag . ']'; //A single open tag with no attributes $start = strpos($content, $start_tag); if ($start !== false) { while ($start !== false) { $shortcode = array( 'atts' => array(), 'content' => '' ); $content_end = $start + strlen($start_tag); //End after the self-closing start tag if ($closing_tag) { $closing_tag_pos = strpos($content, $closing_tag, $content_end); if ($closing_tag_pos !== false) { $content_length = $closing_tag_pos - $content_end; $shortcode['content'] = substr($content, $content_end, $content_length); //Get the content between the opening and closing tag $content_end += strlen($closing_tag); //End after the closing tag } } //If anything was discovered in this shortcode, add it to the return object if (!empty($shortcode['content'])) { $return['shortcodes'][] = $shortcode; } $content = substr($content, $content_end); //Remove this shortcode from the content to continue parsing in the while-loop $start = strpos($content, $start_tag); //Get the position of the next opening tag } } return $return; } /** * Optionally Load Resource * * Usually from within a shortcode, enqueue a script or stylesheet if it hasn't been already. * * @param array $args. An associative array with keys allowed for `type` (`style` or `script` accepted), * `handle` (string), `url` (absolute URL to the resource), `dependencies` (array), `version` (string|false), * `media` (string media type, default is `all`), `inline_styles` (string), in_footer (bool, default is true), * and `localized_data` (array) * @return string The status (if debugging is enabled) */ public static function optionally_load_resource($args) { $args = array_merge(array( 'type' => '', 'handle' => '', //The resource handle 'url' => '', //An absolute URL to the resource to load. If excluded, can only enqueue previously registered resources. 'dependencies' => array(), //An array of handles that the resource depends on 'version' => false, //The version, if any. Default is `false` to exclude a version. 'media' => 'all', //For stylesheets, this parameter is expecting a media type. Default is `all` 'inline_styles' => '', //For stylesheets, add inline styles to a stylesheet when enqueued 'in_footer' => true, //For scripts, this parameter is expecting a boolean value for whether to place script in the footer. Default is true. 'localized_data' => array() //For scripts, additional data to localize a script ), $args); extract($args); // Import variables into the current symbol table from an array $result = ''; if ($type == 'style') { if (wp_style_is($handle, 'registered') && !wp_style_is($handle, 'queue')) { wp_enqueue_style($handle); if ($inline_styles) { wp_add_inline_style($handle, $inline_styles); } if (WP_DEBUG) { $result = sprintf('Stylesheet [%s] is already registered, just enqueue it', $handle); } } elseif (!wp_style_is($handle, 'registered') && !wp_style_is($handle, 'queue')) { if ($url) { wp_enqueue_style($handle, $url, $dependencies, $version, $media); if ($inline_styles) { wp_add_inline_style($handle, $inline_styles); } if (WP_DEBUG) { $result = sprintf('Stylesheet [%s] is NOT enqueued, set it [%s]', $handle, $url); } } elseif (WP_DEBUG) { $result = sprintf('Stylesheet [%s] is NOT enqueued and a URL was not provided to set it', $handle); } } elseif (WP_DEBUG) { $result = sprintf('Stylesheet [%s] is already registered and enqueued, do nothing', $handle); } } elseif ($type == 'script') { if (wp_script_is($handle, 'registered') && !wp_script_is($handle, 'queue')) { wp_enqueue_script($handle); if (WP_DEBUG) { $result = sprintf('Script [%s] is already registered, just enqueue it', $handle); } } elseif (!wp_script_is($handle, 'registered') && !wp_script_is($handle, 'queue')) { if ($url) { wp_enqueue_script($handle, $url, $dependencies, $version, $in_footer); //Place in footer if (WP_DEBUG) { $result = sprintf('Script [%s] is NOT registored OR enqueued, set it [%s]', $handle, $url); } } elseif (WP_DEBUG) { $result = sprintf('Script [%s] is NOT enqueued and a URL was not provided to set it', $handle); } } elseif (WP_DEBUG) { $result = sprintf('Script [%s] is already registered and enqueued, do nothing', $handle); } if (count($localized_data)) { global $wp_scripts; $data = $wp_scripts->get_data($handle, 'data'); if (!empty($data)) { //Localize it wp_localize_script($handle, 'aurise_video_obj', $localized_data); } } } return $result; } /** * Display an object for debugging purposes * * Uses `var_dump()` within an output buffer and surrounds it in a `
` HTML element.
     *
     * @param mixed The variable to dump.
     * @param bool $visible Optional. If true, the `
` element will be visible. If false, it will be hidden. Default is true.
     * @param bool $echo Optional. If true, the content in the output buffer will be echoed. If false, it will be only be returned.
     * @param string $title Optional. An additional title to display before the dumped variable.
     * @param bool $wrapper Optional. If true, the content is wrapped in HTML markup. Otherwise, it is not. Default is true. $visible does not matter if there is no wrapper.
     * @return string The content of the output buffer of the variable dump.
     */
    public static function var_dump($obj, $visible = true, $echo = true, $title = '', $wrapper = true)
    {
        if (WP_DEBUG && WP_DEBUG_DISPLAY) {
            ob_start();
            if ($title) {
                if ($wrapper) {
                    printf('

%s

', $visible ? 'block' : 'none !important', $title); } else { echo ($title . PHP_EOL); } } if ($wrapper) { printf('
', $visible ? 'block' : 'none !important', $title);
            } else {
                echo (PHP_EOL);
            }
            var_dump($obj);
            if ($wrapper) {
                echo ('
'); } else { echo (PHP_EOL); } $output = ob_get_contents(); ob_end_clean(); if ($echo) { echo ($output); } return $output; } return ''; } /** * Write a message to debug.log * * Uses `error_log` * * @param mixed The variable to dump. */ public static function debug_log($obj, $title = '') { if (WP_DEBUG && WP_DEBUG_LOG) { if ($title) { error_log('[ACCESSIBLE READING] ' . $title); } if (is_array($obj) || is_object($obj)) { error_log('[ACCESSIBLE READING] ' . print_r($obj, true)); } else { error_log('[ACCESSIBLE READING] ' . $obj); } } } /** * Normalize HTML * * Sanitizes post content, removes HTML comments, and removes excess whitespace * Documentation: https://developer.wordpress.org/plugins/security/securing-output/ * * @since 1.0.0 * @param string $content * @return string modified content */ public static function normalize_html($content = '') { if ($content) { return self::remove_excess_whitespace(wp_kses_post(self::remove_html_comments($content))); } return $content; } /** * Remove excess whitespace * * @since 1.0.0 * @param string $content * @return string modified content */ public static function remove_excess_whitespace($content = '') { return trim(preg_replace('/\s+/', ' ', $content)); } /** * Remove HTML Comments * * @since 1.0.0 * @param string $content * @return string modified content */ public static function remove_html_comments($content = '') { return preg_replace('//', '', $content); } // /** // * Test to see if executing an AJAX call specific to the WP Migrate DB family of plugins. // * // * @return bool // */ // public static function is_ajax() // { // // must be doing AJAX the WordPress way // if (!defined('DOING_AJAX') || !DOING_AJAX) { // return false; // } // // must be one of our actions -- e.g. core plugin (wpmdb_*), media files (wpmdbmf_*) // if (!isset($_POST['action']) || 0 !== strpos($_POST['action'], 'wpmdb')) { // return false; // } // // must be on blog #1 (first site) if multisite // if (is_multisite() && 1 != get_current_site()->id) { // return false; // } // return true; // } // /** // * Checks if another version of WPMDB(Pro) is active and deactivates it. // * To be hooked on `activated_plugin` so other plugin is deactivated when current plugin is activated. // * // * @param string $plugin // * // */ // public static function deactivate_other_instances($plugin) // { // if (!in_array(basename($plugin), array('wp-migrate-db-pro.php', 'wp-migrate-db.php'))) { // return; // } // $plugin_to_deactivate = 'wp-migrate-db.php'; // $deactivated_notice_id = '1'; // if (basename($plugin) == $plugin_to_deactivate) { // $plugin_to_deactivate = 'wp-migrate-db-pro.php'; // $deactivated_notice_id = '2'; // } // if (is_multisite()) { // $active_plugins = (array) get_site_option('active_sitewide_plugins', array()); // $active_plugins = array_keys($active_plugins); // } else { // $active_plugins = (array) get_option('active_plugins', array()); // } // foreach ($active_plugins as $basename) { // if (false !== strpos($basename, $plugin_to_deactivate)) { // set_transient('wp_migrate_db_deactivated_notice_id', $deactivated_notice_id, 1 * HOUR_IN_SECONDS); // deactivate_plugins($basename); // return; // } // } // } // /** // * Return unserialized object or array // * // * @param string $serialized_string Serialized string. // * @param string $method The name of the caller method. // * // * @return mixed, false on failure // */ // public static function unserialize($serialized_string, $method = '') // { // if (!is_serialized($serialized_string)) { // return false; // } // $serialized_string = trim($serialized_string); // $unserialized_string = @unserialize($serialized_string); // if (false === $unserialized_string && defined('WP_DEBUG_LOG') && WP_DEBUG_LOG) { // $scope = $method ? sprintf(__('Scope: %s().', 'wp-migrate-db'), $method) : false; // $error = sprintf(__('WPMDB Error: Data cannot be unserialized. %s', 'wp-migrate-db'), $scope); // error_log($error); // } // return $unserialized_string; // } // /** // * Use wp_unslash if available, otherwise fall back to stripslashes_deep // * // * @param string|array $arg // * // * @return string|array // */ // public static function safe_wp_unslash($arg) // { // if (function_exists('wp_unslash')) { // return wp_unslash($arg); // } else { // return stripslashes_deep($arg); // } // } // /** // * Display Radio Button HTML // * // * @since 1.0.0 // * @param array $args An associatve array with keys for `name` (string), `value` // * (string of `true` or `false`), `yes` (string), `no` (string), `label`, and // * `reverse` (bool) // * @return string HTML for the checkbox switch // */ // private function get_radio_group($args) // { // $value = esc_attr($args['value']); // $html = sprintf('%s', esc_attr($args['label'])); // if (is_array($args['options']) && count($args['options'])) { // foreach ($args['options'] as $option_value => $option) { // $html .= sprintf( // '', // esc_attr($args['name']), // esc_attr($option_value), // $option_value == $value ? ' selected' : '', // esc_html($option['label']) // ); // } // } // $html .= ''; // return $html; // } // /** // * Display Dropdown HTML // * // * @since 1.0.0 // * @param array $args An associatve array with keys for `name` (string), `value` // * (string of `true` or `false`), `yes` (string), `no` (string), and `label` (string) // * @return string HTML for the checkbox switch // */ // private function get_dropdown($args) // { // $value = esc_attr(strval($args['value'])); // $html = sprintf( // ''; // return $html; // } }