403 ) ); } Queue::enqueue( self::collect_urls() ); $bot = new Bot(); $bot->schedule_process( 1 ); $bot->process_batch(); wp_safe_redirect( add_query_arg( array( 'page' => 'qw-speed-cache-media', 'preloaded' => '1', ), admin_url( 'admin.php' ) ) ); exit; } public function clear_queue() { check_admin_referer( 'qwsc_clear_preload_queue' ); if ( ! current_user_can( 'manage_options' ) ) { wp_die( esc_html__( 'Unauthorized', 'qw-optimize' ), '', array( 'response' => 403 ) ); } Queue::clear(); self::reset_progress(); wp_safe_redirect( add_query_arg( array( 'page' => 'qw-speed-cache-media', 'queue_cleared' => '1', ), admin_url( 'admin.php' ) ) ); exit; } /** * Collect homepage, menus, recent content, homepage links (Free). Sitemap is Pro. * * @return string[] */ public static function collect_urls() { $settings = \QWSpeedCache\Plugin::instance()->get_all_settings(); $max = (int) apply_filters( 'qwsc_preload_url_limit', 500 ); $max = max( 10, min( 2000, $max ) ); $urls = array( home_url( '/' ) ); // Nav menus. $urls = array_merge( $urls, self::urls_from_menus() ); // Recent posts / pages / products (more coverage for G-09). foreach ( array( 'post' => 50, 'page' => 30, 'product' => 30 ) as $pt => $n ) { if ( 'product' === $pt && ! post_type_exists( 'product' ) ) { continue; } $posts = get_posts( array( 'numberposts' => $n, 'post_status' => 'publish', 'post_type' => $pt, ) ); foreach ( $posts as $p ) { $urls[] = get_permalink( $p ); } } // Homepage HTML internal links. $urls = array_merge( $urls, self::urls_from_homepage_html() ); // Sitemap preload is Pro-only — not collected in Free. $host = wp_parse_url( home_url(), PHP_URL_HOST ); $out = array(); foreach ( $urls as $url ) { $url = Queue::normalize_url( is_string( $url ) ? $url : '' ); if ( '' === $url ) { continue; } $u_host = wp_parse_url( $url, PHP_URL_HOST ); if ( $u_host && strtolower( (string) $u_host ) === strtolower( (string) $host ) ) { $out[] = $url; } } $out = \QWSpeedCache\Cache\Warmup::filter_safe( $out ); // Neutral extension point: Pro may supply traffic-aware ordering. $out = apply_filters( 'qwsc_preload_order_urls', $out ); if ( ! is_array( $out ) ) { $out = array(); } $out = \QWSpeedCache\Cache\Warmup::filter_safe( $out ); $out = array_values( array_unique( $out ) ); if ( count( $out ) > $max ) { $out = array_slice( $out, 0, $max ); } self::bump_progress_on_seed( count( $out ) ); return $out; } /** * @return string[] */ public static function urls_from_menus() { $urls = array(); if ( ! function_exists( 'wp_get_nav_menus' ) ) { return $urls; } $menus = wp_get_nav_menus(); if ( empty( $menus ) || is_wp_error( $menus ) ) { return $urls; } foreach ( $menus as $menu ) { $items = wp_get_nav_menu_items( $menu->term_id ); if ( empty( $items ) || is_wp_error( $items ) ) { continue; } foreach ( $items as $item ) { if ( ! empty( $item->url ) ) { $urls[] = $item->url; } } } return $urls; } /** * Extract same-site hrefs from homepage HTML. * * @return string[] */ public static function urls_from_homepage_html() { $response = wp_safe_remote_get( home_url( '/' ), array( 'timeout' => 8, 'sslverify' => true, ) ); if ( is_wp_error( $response ) || 200 !== (int) wp_remote_retrieve_response_code( $response ) ) { return array(); } $body = (string) wp_remote_retrieve_body( $response ); return self::extract_hrefs( $body, home_url( '/' ) ); } /** * Pure-ish: extract absolute URLs from HTML href attributes. * * @param string $html HTML. * @param string $base Base URL for relative links. * @return string[] */ public static function extract_hrefs( $html, $base ) { $out = array(); if ( ! is_string( $html ) || '' === $html ) { return $out; } if ( ! preg_match_all( '/href\s*=\s*[\'"]([^\'"#]+)[\'"]/i', $html, $m ) ) { return $out; } $base_host = wp_parse_url( $base, PHP_URL_HOST ); foreach ( $m[1] as $href ) { $href = trim( $href ); if ( '' === $href || 0 === strpos( $href, 'mailto:' ) || 0 === strpos( $href, 'tel:' ) || 0 === strpos( $href, 'javascript:' ) ) { continue; } if ( 0 === strpos( $href, '//' ) ) { $href = ( is_ssl() ? 'https:' : 'http:' ) . $href; } elseif ( 0 === strpos( $href, '/' ) ) { $href = home_url( $href ); } elseif ( ! preg_match( '#^https?://#i', $href ) ) { continue; } $h = wp_parse_url( $href, PHP_URL_HOST ); if ( $h && $base_host && strtolower( (string) $h ) === strtolower( (string) $base_host ) ) { $out[] = $href; } } return $out; } /** * @param int $seeded Count seeded. */ public static function bump_progress_on_seed( $seeded ) { $stats = array( 'seeded' => (int) $seeded, 'done' => 0, 'failed' => 0, 'updated' => time(), 'status' => $seeded > 0 ? 'running' : 'idle', ); update_option( 'qwsc_preload_stats', $stats, false ); } /** * @param int $done Success count delta. * @param int $failed Fail count delta. * @param int $remaining Queue remaining. */ public static function bump_progress( $done, $failed, $remaining ) { $stats = get_option( 'qwsc_preload_stats', array() ); if ( ! is_array( $stats ) ) { $stats = array(); } $stats['done'] = (int) ( $stats['done'] ?? 0 ) + (int) $done; $stats['failed'] = (int) ( $stats['failed'] ?? 0 ) + (int) $failed; $stats['updated'] = time(); $stats['status'] = $remaining > 0 ? 'running' : 'done'; update_option( 'qwsc_preload_stats', $stats, false ); } public static function reset_progress() { update_option( 'qwsc_preload_stats', array( 'seeded' => 0, 'done' => 0, 'failed' => 0, 'updated' => time(), 'status' => 'idle', ), false ); } /** * @return array{seeded:int,done:int,failed:int,status:string,updated:int} */ public static function progress() { $stats = get_option( 'qwsc_preload_stats', array() ); if ( ! is_array( $stats ) ) { $stats = array(); } return array( 'seeded' => (int) ( $stats['seeded'] ?? 0 ), 'done' => (int) ( $stats['done'] ?? 0 ), 'failed' => (int) ( $stats['failed'] ?? 0 ), 'status' => (string) ( $stats['status'] ?? 'idle' ), 'updated' => (int) ( $stats['updated'] ?? 0 ), 'queue' => Queue::count(), ); } }