\]#i', // Replace `true` with `!0` '#(?<=return |[=:,\(\[])true\b#', // Replace `false` with `!1` '#(?<=return |[=:,\(\[])false\b#', // Clean up ... '#\s*(\/\*|\*\/)\s*#' ), array( '$1', '$1$2', '}', '$1$3', '$1.$3', '!0', '!1', '$1' ), $input ); } /** * Minify CSS * * @see https://gist.github.com/tovic/d7b310dea3b33e4732c0 * * @param string * * @return string */ public static function minifyCSS( $input ) { if ( trim( $input ) === "" ) { return $input; } // Force white-space(s) in `calc()` if ( strpos( $input, 'calc(' ) !== false ) { $input = preg_replace_callback( '#(?<=[\s:])calc\(\s*(.*?)\s*\)#', function ( $matches ) { return 'calc(' . preg_replace( '#\s+#', "\x1A", $matches[1] ) . ')'; }, $input ); } return preg_replace( array( // Remove comment(s) '#("(?:[^"\\\]++|\\\.)*+"|\'(?:[^\'\\\\]++|\\\.)*+\')|\/\*(?!\!)(?>.*?\*\/)|^\s*|\s*$#s', // Remove unused white-space(s) '#("(?:[^"\\\]++|\\\.)*+"|\'(?:[^\'\\\\]++|\\\.)*+\'|\/\*(?>.*?\*\/))|\s*+;\s*+(})\s*+|\s*+([*$~^|]?+=|[{};,>~+]|\s*+-(?![0-9\.])|!important\b)\s*+|([[(:])\s++|\s++([])])|\s++(:)\s*+(?!(?>[^{}"\']++|"(?:[^"\\\]++|\\\.)*+"|\'(?:[^\'\\\\]++|\\\.)*+\')*+{)|^\s++|\s++\z|(\s)\s+#si', // Replace `0(cm|em|ex|in|mm|pc|pt|px|vh|vw|%)` with `0` '#(?<=[\s:])(0)(cm|em|ex|in|mm|pc|pt|px|vh|vw|%)#si', // Replace `:0 0 0 0` with `:0` '#:(0\s+0|0\s+0\s+0\s+0)(?=[;\}]|\!important)#i', // Replace `background-position:0` with `background-position:0 0` '#(background-position):0(?=[;\}])#si', // Replace `0.6` with `.6`, but only when preceded by a white-space or `=`, `:`, `,`, `(`, `-` '#(?<=[\s=:,\(\-]|&\#32;)0+\.(\d+)#s', // Minify string value '#(\/\*(?>.*?\*\/))|(?.*?\*\/))|(\burl\()([\'"])([^\s]+?)\3(\))#si', // Minify HEX color code '#(?<=[\s=:,\(]\#)([a-f0-6]+)\1([a-f0-6]+)\2([a-f0-6]+)\3#i', // Replace `(border|outline):none` with `(border|outline):0` '#(?<=[\{;])(border|outline):none(?=[;\}\!])#', // Remove empty selector(s) '#(\/\*(?>.*?\*\/))|(^|[\{\}])(?:[^\s\{\}]+)\{\}#s', '#\x1A#' ), array( '$1', '$1$2$3$4$5$6$7', '$1', ':0', '$1:0 0', '.$1', '$1$3', '$1$2$4$5', '$1$2$3', '$1:0', '$1$2', ' ' ), $input ); } /** * Compare WooCommerce function * * @param $version * @param $op * * @return bool */ public static function compareWcVersion( $version, $op ) { if ( function_exists( 'WC' ) && ( version_compare( WC()->version, $version, $op ) ) ) { return true; } return false; } /** * Check if is settings page * @return bool */ public static function isSettingsPage() { if ( is_admin() && ! empty( $_GET['page'] ) && $_GET['page'] === 'dgwt_wcas_settings' ) { return true; } return false; } /** * Check if is debug page * @return bool */ public static function isDebugPage() { if ( is_admin() && ! empty( $_GET['page'] ) && $_GET['page'] === 'dgwt_wcas_debug' ) { return true; } return false; } /** * Check if is Freemius checkout page * @return bool */ public static function isCheckoutPage() { if ( is_admin() && ! empty( $_GET['page'] ) && $_GET['page'] === 'dgwt_wcas_settings-pricing' ) { return true; } return false; } /** * Get settings URL * * @return string */ public static function getSettingsUrl() { return admin_url( 'admin.php?page=dgwt_wcas_settings' ); } /** * Get total products * * @return int */ public static function getTotalProducts() { global $wpdb; $sql = "SELECT COUNT(ID) FROM $wpdb->posts WHERE post_type = 'product' AND post_status = 'publish'"; $total = $wpdb->get_var( $sql ); return absint( $total ); } /** * Get all products IDs * @return array */ public static function getProductsForIndex() { global $wpdb; $sql = "SELECT ID FROM $wpdb->posts WHERE post_type = 'product' AND post_status = 'publish' ORDER BY ID ASC"; $ids = $wpdb->get_col( $sql ); if ( ! is_array( $ids ) || empty( $ids[0] ) || ! is_numeric( $ids[0] ) ) { $ids = array(); } return $ids; } /** * Get readable format of memory * * @param int $bytes * * @return string */ public static function getReadableMemorySize( $bytes ) { $unit = array( 'b', 'kb', 'mb', 'gb', 'tb', 'pb' ); return @round( $bytes / pow( 1024, ( $i = floor( log( $bytes, 1024 ) ) ) ), 2 ) . ' ' . $unit[ $i ]; } /** * Get pro icon/label * * @param string $label * @param string $type * @param string $headerSubtitle * * @return string */ public static function getSettingsProLabel( $label, $type = 'header', $headerSubtitle = '' ) { $html = ''; switch ( $type ) { case 'header': if ( ! empty( $headerSubtitle ) ) { $label = '' . $label . '' . $headerSubtitle . ''; } $html .= '
' . $label . '' . __( 'Pro', 'ajax-search-for-woocommerce' ) . '
'; break; case 'option-label': $html .= '
' . $label . '' . __( 'Pro', 'ajax-search-for-woocommerce' ) . '
'; break; } return $html; } /** * Calc score for searched * * @param string $searched * @param string $string eg. product title * @param array $args * * @return int */ public static function calcScore( $searched, $string, $args = array() ) { $score = 0; if ( empty( $searched ) || empty( $string ) ) { return $score; } $default = array( 'check_similarity' => true, 'check_position' => true, 'score_containing' => 50 ); $args = array_merge( $default, $args ); $searched = mb_strtolower( $searched ); $string = mb_strtolower( $string ); if ( $args['check_similarity'] ) { $m = similar_text( $searched, $string, $percent ); $score = ($score + $percent) / 3; } $pos = strpos( $string, $searched ); // Add score based on substring position if ( $pos !== false ) { $score += $args['score_containing']; // Bonus for contained substring // Bonus for substring position if ( $args['check_position'] ) { $posBonus = ( 100 - ( $pos * 100 ) / strlen( $string ) ) / 2; $score += $posBonus; } // Bonus for exact match if ( $string === $searched ) { $score += ($args['score_containing'] * 5); } } return $score; } /** * Sorting by score * * @param $a * @param $b * * @return int */ public static function cmpSimilarity( $a, $b ) { $scoreA = 0; $scoreB = 0; if ( is_object( $a ) ) { $scoreA = $a->score; $scoreB = $b->score; } if ( is_array( $a ) ) { $scoreA = $a['score']; $scoreB = $b['score']; } if ( $scoreA == $scoreB ) { return 0; } return ( $scoreA < $scoreB ) ? 1 : - 1; } /** * Sorting by search resutls groups priority * * @param $a * @param $b * * @return int */ public static function sortAjaxResutlsGroups( $a, $b ) { if ( $a['order'] == $b['order'] ) { return 0; } return ( $a['order'] < $b['order'] ) ? - 1 : 1; } /** * Sort from the longest to the shortest * * @param $a * @param $b * * @return int */ public static function sortFromLongest( $a, $b ) { $la = mb_strlen( $a ); $lb = mb_strlen( $b ); if ( $la == $lb ) { return strcmp( $b, $a ); } return $lb - $la; } /** * Get taxonomy parents * * @param int $term_id * @param string $taxonomy * * @return string */ public static function getTermBreadcrumbs( $termID, $taxonomy, $visited = array(), $lang = '', $exclude = array() ) { $chain = ''; $separator = ' > '; if ( Multilingual::isMultilingual() ) { $parent = Multilingual::getTerm( $termID, $taxonomy, $lang ); } else { $parent = get_term( $termID, $taxonomy ); } if ( empty( $parent ) || ! isset( $parent->name ) ) { return ''; } $name = $parent->name; if ( $parent->parent && ( $parent->parent != $parent->term_id ) && ! in_array( $parent->parent, $visited ) ) { $visited[] = $parent->parent; $chain .= self::getTermBreadcrumbs( $parent->parent, $taxonomy, $visited, $lang ); } if ( ! in_array( $parent->term_id, $exclude ) ) { $chain .= $name . $separator; } return $chain; } /** * Get taxonomies of products attributes * * @return array * */ public static function getAttributesTaxonomies() { $taxonomies = array(); $attributeTaxonomies = wc_get_attribute_taxonomies(); if ( ! empty( $attributeTaxonomies ) ) { foreach ( $attributeTaxonomies as $taxonomy ) { $taxonomies[] = 'pa_' . $taxonomy->attribute_name; } } return apply_filters( 'dgwt/wcas/attribute_taxonomies', $taxonomies ); } /** * */ public static function canInstallPremium() { } /** * Get indexer demo HTML * * @return string */ public static function indexerDemoHtml() { $html = ''; ob_start(); include DGWT_WCAS_DIR . 'partials/admin/indexer-header-demo.php'; $html .= ob_get_clean(); return $html; } /** * Get features HTML * * @return string */ public static function featuresHtml() { $html = ''; ob_start(); include DGWT_WCAS_DIR . 'partials/admin/features.php'; $html .= ob_get_clean(); return $html; } /** * Get searchable custom fields keys * * @return array */ public static function getSearchableCustomFields() { global $wpdb; $customFields = array(); $excludedMetaKeys = array( '_sku', '_wp_old_date', '_tax_status', '_stock_status', '_product_version', '_smooth_slider_style', 'auctioninc_calc_method', 'auctioninc_pack_method', '_thumbnail_id', '_product_image_gallery', 'pdf_download', 'slide_template', 'cad_iframe', 'downloads', 'edrawings_file', '3d_pdf_download', '3d_pdf_render', '_original_id' ); $excludedMetaKeys = apply_filters( 'dgwt/wcas/indexer/excluded_meta_keys', $excludedMetaKeys ); $sql = "SELECT DISTINCT meta_key FROM $wpdb->postmeta as pm INNER JOIN $wpdb->posts as p ON p.ID = pm.post_id WHERE p.post_type = 'product' AND pm.meta_value NOT LIKE 'field_%' AND pm.meta_value NOT LIKE 'a:%' AND pm.meta_value NOT LIKE '%\%\%%' AND pm.meta_value NOT LIKE '_oembed_%' AND pm.meta_value NOT REGEXP '^1[0-9]{9}' AND pm.meta_value NOT IN ('1','0','-1','no','yes','[]', '') "; $metaKeys = $wpdb->get_col( $sql ); if ( ! empty( $metaKeys ) ) { foreach ( $metaKeys as $metaKey ) { if ( ! in_array( $metaKey, $excludedMetaKeys ) && self::keyIsValid( $metaKey ) ) { $label = $metaKey; //@TODO Recognize labels based on meta key or public known as Yoast SEO etc. $customFields[] = array( 'label' => $label, 'key' => $label ); } } } $customFields = array_reverse( $customFields ); return apply_filters( 'dgwt/wcas/indexer/searchable_custom_fields', $customFields ); } /** * Check if key is valid * * @param $key * * @return bool */ public static function keyIsValid( $key ) { return ! preg_match( '/[^\p{L}\p{N}\:\.\_\s\-]+/u', $key ); } /** * Check if table exist * * @return bool */ public static function isTableExists( $tableName ) { global $wpdb; $exist = false; $wpdb->hide_errors(); $sql = $wpdb->prepare( "SHOW TABLES LIKE %s", $tableName ); if ( ! empty( $wpdb->get_var( $sql ) ) ) { $exist = true; } return $exist; } /** * Check if the engine can search in variable products * * @return bool */ public static function canSearchInVariableProducts() { global $wpdb; $allow = false; $el = $wpdb->get_var( "SELECT ID FROM $wpdb->posts WHERE post_type = 'product_variation' LIMIT 1" ); if ( ! empty( $el ) && is_numeric( $el ) ) { $allow = true; } if ( DGWT_WCAS()->settings->getOption( 'search_in_product_content' ) !== 'on' ) { $allow = false; } return apply_filters( 'dgwt/wcas/search_in_variable_products', $allow ); } /** * Remove batch options * * @return void */ public static function removeBatchOptions__premium_only() { global $wpdb; if ( dgoraAsfwFs()->is__premium_only() ) { /** * Searchable and readable index batch * Samples: * - wcas_build_readable_index_batch_bfc6f612423866e94a9fd52889a635b8 * - wcas_build_searchable_index_batch_bfc6f612423866e94a9fd52889a635b8 */ $table = $wpdb->options; $column = 'option_name'; if ( is_multisite() ) { $table = $wpdb->sitemeta; $column = 'meta_key'; } $wpdb->query( " DELETE FROM {$table} WHERE {$column} LIKE '%_index_batch_%' OR {$column} LIKE '%wcas_build_readable%' OR {$column} LIKE '%wcas_build_searchable%' OR {$column} LIKE '%wcas_build_variation%' OR {$column} LIKE '%wcas_build_taxonomy%' " ); } } /** * Allow to remove method for an hook when, it's a class method used and class don't have variable, but you know the class name * * @link https://github.com/herewithme/wp-filters-extras * @return bool */ public static function removeFiltersForAnonymousClass( $hook_name = '', $class_name = '', $method_name = '', $priority = 0 ) { global $wp_filter; // Take only filters on right hook name and priority if ( ! isset( $wp_filter[ $hook_name ][ $priority ] ) || ! is_array( $wp_filter[ $hook_name ][ $priority ] ) ) { return false; } // Loop on filters registered foreach ( (array) $wp_filter[ $hook_name ][ $priority ] as $unique_id => $filter_array ) { // Test if filter is an array ! (always for class/method) if ( isset( $filter_array['function'] ) && is_array( $filter_array['function'] ) ) { // Test if object is a class, class and method is equal to param ! if ( is_object( $filter_array['function'][0] ) && get_class( $filter_array['function'][0] ) && get_class( $filter_array['function'][0] ) == $class_name && $filter_array['function'][1] == $method_name ) { // Test for WordPress >= 4.7 WP_Hook class (https://make.wordpress.org/core/2016/09/08/wp_hook-next-generation-actions-and-filters/) if ( is_a( $wp_filter[ $hook_name ], 'WP_Hook' ) ) { unset( $wp_filter[ $hook_name ]->callbacks[ $priority ][ $unique_id ] ); } else { unset( $wp_filter[ $hook_name ][ $priority ][ $unique_id ] ); } } } } return false; } /** * Create HTML question mark with tooltip * * @param string $id * @param string $content * @param string $template * * @return string */ public static function createQuestionMark( $id, $content = '', $template = '', $placement = 'top' ) { if ( ! empty( $template ) ) { $file = DGWT_WCAS_DIR . 'partials/admin/tooltips/' . $template . '.php'; if ( file_exists( $file ) ) { ob_start(); require $file; $content = ob_get_contents(); ob_end_clean(); } } $id = 'js-dgwt-wcas-tooltip-id' . sanitize_key( $id ); $html = '
'; $html .= ''; return $html; } /** * Get list of 24 hours */ public static function getHours() { $hours = array(); $cycle12 = get_option( 'time_format' ) === 'H:i' ? false : true; for ( $i = 0; $i < 24; $i ++ ) { $label = $cycle12 ? $i . ':00 am' : $i . ':00'; if ( $cycle12 && $i === 0 ) { $label = 12 . ':00 am'; } if ( $cycle12 && $i > 11 ) { if ( $i === 12 ) { $label = 12 . ':00 pm'; } else { $label = $i - 12 . ':00 pm'; } } $hours[ $i ] = $label; } return $hours; } /** * Get local date including timezone * * @param $timestamp * @param string $format * * @return string * @throws \Exception */ public static function localDate( $timestamp, $format = '' ) { if ( empty( $format ) ) { $format = get_option( 'date_format' ) . ' ' . get_option( 'time_format' ); } $date = new \WC_DateTime( "@$timestamp" ); $date->setTimezone( new \DateTimeZone( wc_timezone_string() ) ); return $date->date_i18n( $format ); } /** * Get labels * * @return array */ public static function getLabels() { $labels = apply_filters( 'dgwt/wcas/labels', array( 'category' => __( 'Category', 'woocommerce' ), 'tag' => __( 'Tag' ), 'brand' => __( 'Brand', 'ajax-search-for-woocommerce' ), 'post' => __( 'Post' ), 'page' => __( 'Page' ), 'vendor' => __( 'Vendor', 'ajax-search-for-woocommerce' ), 'product_cat_plu' => __( 'Categories', 'woocommerce' ), 'product_tag_plu' => __( 'Tags' ), 'product_plu' => __( 'Products', 'woocommerce' ), 'brand_plu' => __( 'Brands', 'ajax-search-for-woocommerce' ), 'post_plu' => __( 'Posts' ), 'page_plu' => __( 'Pages' ), 'vendor_plu' => __( 'Vendors', 'ajax-search-for-woocommerce' ), 'sku_label' => __( 'SKU', 'woocommerce' ) . ':', 'sale_badge' => __( 'Sale', 'woocommerce' ), 'vendor_sold_by' => __( 'Sold by:', 'ajax-search-for-woocommerce' ), 'featured_badge' => __( 'Featured', 'woocommerce' ), 'in' => _x( 'in', 'in categories fe. in Books > Crime stories', 'ajax-search-for-woocommerce' ), 'read_more' => __( 'continue reading', 'ajax-search-for-woocommerce' ), 'no_results' => DGWT_WCAS()->settings->getOption( 'search_no_results_text', __( 'No results', 'ajax-search-for-woocommerce' ) ), 'show_more' => DGWT_WCAS()->settings->getOption( 'search_see_all_results_text', __( 'See all products...', 'ajax-search-for-woocommerce' ) ), 'show_more_details' => DGWT_WCAS()->settings->getOption( 'search_see_all_results_text', __( 'See all products...', 'ajax-search-for-woocommerce' ) ), 'search_placeholder' => DGWT_WCAS()->settings->getOption( 'search_placeholder', __( 'Search for products...', 'ajax-search-for-woocommerce' ) ), 'submit' => DGWT_WCAS()->settings->getOption( 'search_submit_text', '' ) ) ); return $labels; } /** * Get labels * * @param string $key * * @return string */ public static function getLabel( $key ) { $label = ''; $labels = self::getLabels(); if ( array_key_exists( $key, $labels ) ) { $label = $labels[ $key ]; } return $label; } /** * Remove all HTML tags including