$rewrite ) { if ( false !== strpos( $rewrite, 'embed=true' ) ) { unset( $rules[ $rule ] ); } } return $rules; } /** * Disable wp-embed.js script * * Dequeues the wp-embed.js script that WordPress loads by default. * This script is used for embedding WordPress posts on other sites. * * @since 1.0.0 */ function roi_dequeue_embed_script() { wp_deregister_script( 'wp-embed' ); } add_action( 'wp_footer', 'roi_dequeue_embed_script' ); /** * Disable WordPress Feeds * * Removes RSS, RDF, and Atom feeds. * Only disable if you don't need feed functionality. * * @since 1.0.0 */ function roi_disable_feeds() { wp_die( esc_html__( 'No feed available, please visit our homepage!', 'roi' ), esc_html__( 'Feed Not Available', 'roi' ), array( 'response' => 404, 'back_link' => true, ) ); } /** * Remove feed links and redirect feed URLs * * @since 1.0.0 */ function roi_disable_feed_links() { // Remove feed links from header. remove_action( 'wp_head', 'feed_links', 2 ); remove_action( 'wp_head', 'feed_links_extra', 3 ); // Redirect feed URLs to homepage. add_action( 'do_feed', 'roi_disable_feeds', 1 ); add_action( 'do_feed_rdf', 'roi_disable_feeds', 1 ); add_action( 'do_feed_rss', 'roi_disable_feeds', 1 ); add_action( 'do_feed_rss2', 'roi_disable_feeds', 1 ); add_action( 'do_feed_atom', 'roi_disable_feeds', 1 ); add_action( 'do_feed_rss2_comments', 'roi_disable_feeds', 1 ); add_action( 'do_feed_atom_comments', 'roi_disable_feeds', 1 ); } add_action( 'init', 'roi_disable_feed_links' ); /** * Disable RSD and Windows Live Writer Manifest * * Really Simple Discovery (RSD) and Windows Live Writer (WLW) manifest * are rarely used and can be safely removed. * * @since 1.0.0 */ function roi_disable_rsd_wlw() { // Remove RSD link. remove_action( 'wp_head', 'rsd_link' ); // Remove Windows Live Writer manifest link. remove_action( 'wp_head', 'wlwmanifest_link' ); } add_action( 'init', 'roi_disable_rsd_wlw' ); /** * Disable Dashicons for non-logged users * * Dashicons are only needed in the admin area and for logged-in users. * This removes them from the front-end for visitors. * * @since 1.0.0 */ function roi_disable_dashicons() { if ( ! is_user_logged_in() ) { wp_deregister_style( 'dashicons' ); } } add_action( 'wp_enqueue_scripts', 'roi_disable_dashicons' ); /** * Disable Block Library CSS * * Removes the default WordPress block library styles. * Only disable if you're not using the block editor or if you're * providing your own block styles. * * @since 1.0.0 */ function roi_disable_block_library_css() { // Remove default block library styles. wp_dequeue_style( 'wp-block-library' ); wp_dequeue_style( 'wp-block-library-theme' ); // Remove inline global styles. wp_dequeue_style( 'global-styles' ); // Remove classic theme styles (if not using classic editor). wp_dequeue_style( 'classic-theme-styles' ); } add_action( 'wp_enqueue_scripts', 'roi_disable_block_library_css', 100 ); /** * Remove WordPress version from head and feeds * * Removes the WordPress version number for security reasons. * * @since 1.0.0 */ function roi_remove_wp_version() { return ''; } add_filter( 'the_generator', 'roi_remove_wp_version' ); /** * Disable XML-RPC * * XML-RPC is often targeted by brute force attacks. * Disable if you don't need remote publishing functionality. * * @since 1.0.0 * @return bool */ function roi_disable_xmlrpc() { return false; } add_filter( 'xmlrpc_enabled', 'roi_disable_xmlrpc' ); /** * Remove jQuery Migrate * * jQuery Migrate is used for backwards compatibility but adds extra overhead. * Only remove if you've verified all scripts work without it. * * @since 1.0.0 * @param WP_Scripts $scripts WP_Scripts object. */ function roi_remove_jquery_migrate( $scripts ) { if ( ! is_admin() && isset( $scripts->registered['jquery'] ) ) { $script = $scripts->registered['jquery']; if ( $script->deps ) { // Remove jquery-migrate from dependencies. $script->deps = array_diff( $script->deps, array( 'jquery-migrate' ) ); } } } add_action( 'wp_default_scripts', 'roi_remove_jquery_migrate' ); /** * Optimize WordPress Database Queries * * Removes unnecessary meta queries for better performance. * * @since 1.0.0 */ function roi_optimize_queries() { // Remove unnecessary post meta from queries remove_action( 'wp_head', 'adjacent_posts_rel_link_wp_head', 10 ); remove_action( 'wp_head', 'wp_shortlink_wp_head', 10 ); } add_action( 'init', 'roi_optimize_queries' ); /** * Disable WordPress Admin Bar for Non-Admins * * Reduces HTTP requests for non-admin users. * * @since 1.0.0 */ function roi_disable_admin_bar() { if ( ! current_user_can( 'administrator' ) && ! is_admin() ) { show_admin_bar( false ); } } add_action( 'after_setup_theme', 'roi_disable_admin_bar' ); /** * ============================================================================ * RESOURCE HINTS: DNS PREFETCH, PRECONNECT, PRELOAD * ============================================================================ */ /** * Agregar DNS Prefetch y Preconnect para recursos externos * * DNS Prefetch: Resuelve DNS antes de que se necesite el recurso * Preconnect: Establece conexión completa (DNS + TCP + TLS) por anticipado * * @since 1.0.0 * @param array $urls Array of resource URLs. * @param string $relation_type The relation type (dns-prefetch, preconnect, etc.). * @return array Modified array of resource URLs. */ function roi_add_resource_hints( $urls, $relation_type ) { // DNS Prefetch para recursos externos que no son críticos if ( 'dns-prefetch' === $relation_type ) { // CDN de Bootstrap Icons (ya usado en enqueue-scripts.php) $urls[] = 'https://cdn.jsdelivr.net'; // Google Analytics (si se usa) $urls[] = 'https://www.google-analytics.com'; $urls[] = 'https://www.googletagmanager.com'; // Google AdSense (si se usa) $urls[] = 'https://pagead2.googlesyndication.com'; $urls[] = 'https://adservice.google.com'; $urls[] = 'https://googleads.g.doubleclick.net'; } // Preconnect para recursos críticos externos if ( 'preconnect' === $relation_type ) { // CDN de Bootstrap Icons - recurso crítico usado en el header $urls[] = array( 'href' => 'https://cdn.jsdelivr.net', 'crossorigin' => 'anonymous', ); } return $urls; } add_filter( 'wp_resource_hints', 'roi_add_resource_hints', 10, 2 ); /** * Preload de recursos críticos para mejorar LCP y reducir CLS * * Preload indica al navegador que descargue recursos críticos lo antes posible. * Útil para fuentes, CSS crítico, y imágenes hero. * * Fase 4.3 PageSpeed: Preload de fuentes .woff2 para reducir CLS * - Sin preload: navegador descubre fuentes después de parsear CSS (tarde) * - Con preload: fuentes se descargan en paralelo con CSS (temprano) * * @since 1.0.0 * @updated 1.0.22 Agregado preload de fuentes Poppins para reducir CLS */ function roi_preload_critical_resources() { $theme_uri = get_template_directory_uri(); // Preload del CSS de Bootstrap SUBSET (crítico para el layout) // IMPORTANTE: Debe coincidir con el archivo encolado en enqueue-scripts.php printf( '' . "\n", esc_url( $theme_uri . '/Assets/Vendor/Bootstrap/Css/bootstrap-subset.min.css' ) ); // Preload del CSS de fuentes (crítico para evitar FOIT/FOUT) printf( '' . "\n", esc_url( $theme_uri . '/Assets/Css/css-global-fonts.css' ) ); // Fase 4.3 PageSpeed: Preload de TODAS las fuentes Poppins para eliminar CLS // Regular (400) - texto del body printf( '' . "\n", esc_url( $theme_uri . '/Assets/Fonts/poppins-v24-latin-regular.woff2' ) ); // Medium (500) - usado en algunos elementos printf( '' . "\n", esc_url( $theme_uri . '/Assets/Fonts/poppins-v24-latin-500.woff2' ) ); // Semibold (600) - headings y elementos destacados printf( '' . "\n", esc_url( $theme_uri . '/Assets/Fonts/poppins-v24-latin-600.woff2' ) ); // Bold (700) - títulos principales printf( '' . "\n", esc_url( $theme_uri . '/Assets/Fonts/poppins-v24-latin-700.woff2' ) ); // Bootstrap Icons font printf( '' . "\n", esc_url( $theme_uri . '/Assets/Vendor/fonts/bootstrap-icons-subset.woff2' ) ); } add_action( 'wp_head', 'roi_preload_critical_resources', 2 ); /** * ============================================================================ * OPTIMIZACIÓN DE SCRIPTS Y ESTILOS * ============================================================================ */ /** * Agregar atributos async/defer a scripts específicos * * Los scripts con defer se descargan en paralelo pero se ejecutan en orden * después de que el DOM esté listo. * * @since 1.0.0 * @param string $tag The script tag. * @param string $handle The script handle. * @return string Modified script tag. */ function roi_add_script_attributes( $tag, $handle ) { // Scripts que deben tener async (no dependen de otros ni del DOM) $async_scripts = array( // Google Analytics u otros scripts de tracking 'google-analytics', 'gtag', ); // Scripts que ya tienen defer via strategy en wp_enqueue_script // No necesitamos modificarlos aquí ya que WordPress 6.3+ lo maneja if ( in_array( $handle, $async_scripts, true ) ) { // Agregar async solo si no tiene defer if ( false === strpos( $tag, 'defer' ) ) { $tag = str_replace( ' src', ' async src', $tag ); } } return $tag; } add_filter( 'script_loader_tag', 'roi_add_script_attributes', 10, 2 ); /** * Optimizar el Heartbeat API de WordPress * * El Heartbeat API hace llamadas AJAX periódicas que pueden afectar el rendimiento. * Lo desactivamos en el frontend y lo ralentizamos en el admin. * * @since 1.0.0 */ function roi_optimize_heartbeat() { // Desactivar completamente en el frontend if ( ! is_admin() ) { wp_deregister_script( 'heartbeat' ); } } add_action( 'init', 'roi_optimize_heartbeat', 1 ); /** * Modificar configuración del Heartbeat en admin * * @since 1.0.0 * @param array $settings Heartbeat settings. * @return array Modified settings. */ function roi_modify_heartbeat_settings( $settings ) { // Cambiar intervalo de 15 segundos (default) a 60 segundos $settings['interval'] = 60; return $settings; } add_filter( 'heartbeat_settings', 'roi_modify_heartbeat_settings' ); /** * ============================================================================ * OPTIMIZACIÓN DE BASE DE DATOS Y QUERIES * ============================================================================ */ /** * Limitar revisiones de posts para reducir tamaño de BD * * Esto se debe configurar en wp-config.php, pero lo documentamos aquí: * define('WP_POST_REVISIONS', 5); * * @since 1.0.0 */ /** * Optimizar WP_Query para posts relacionados y listados * * @since 1.0.0 * @param WP_Query $query The WP_Query instance. */ function roi_optimize_main_query( $query ) { // Solo en queries principales en el frontend if ( is_admin() || ! $query->is_main_query() ) { return; } // En archivos, limitar posts por página para mejorar rendimiento if ( $query->is_archive() || $query->is_home() ) { // No cargar meta innecesaria $query->set( 'update_post_meta_cache', true ); $query->set( 'update_post_term_cache', true ); // Limitar posts por página si no está configurado if ( ! $query->get( 'posts_per_page' ) ) { $query->set( 'posts_per_page', 12 ); } } } add_action( 'pre_get_posts', 'roi_optimize_main_query' ); /** * Deshabilitar self-pingbacks * * Los self-pingbacks ocurren cuando un post enlaza a otro post del mismo sitio. * Son innecesarios y generan queries adicionales. * * @since 1.0.0 * @param array $links An array of post links to ping. * @return array Modified array without self-pings. */ function roi_disable_self_pingbacks( &$links ) { $home = get_option( 'home' ); foreach ( $links as $l => $link ) { if ( 0 === strpos( $link, $home ) ) { unset( $links[ $l ] ); } } } add_action( 'pre_ping', 'roi_disable_self_pingbacks' ); /** * ============================================================================ * OPTIMIZACIÓN DE RENDER Y LAYOUT * ============================================================================ */ /** * Agregar display=swap a Google Fonts para evitar FOIT * * Ya no usamos Google Fonts (fuentes locales), pero dejamos la función * por si se necesita en el futuro. * * @since 1.0.0 * @param string $src The source URL. * @return string Modified source URL. */ function roi_add_font_display_swap( $src ) { if ( strpos( $src, 'fonts.googleapis.com' ) !== false ) { $src = add_query_arg( 'display', 'swap', $src ); } return $src; } add_filter( 'style_loader_src', 'roi_add_font_display_swap' ); /** * Agregar width y height a imágenes para prevenir CLS * * WordPress 5.5+ agrega automáticamente width/height, pero aseguramos que esté activo. * * @since 1.0.0 * @return bool */ function roi_enable_image_dimensions() { return true; } add_filter( 'wp_lazy_loading_enabled', 'roi_enable_image_dimensions' ); /** * Optimizar buffer de salida HTML * * DESACTIVADO: Esta función causa conflicto con W3 Total Cache. * Cuando zlib.output_compression está activo, W3TC no puede cachear * las páginas porque recibe "Response is compressed". * * La compresión GZIP la maneja nginx a nivel de servidor. * * @since 1.0.0 * @deprecated 1.0.1 Conflicto con W3TC page cache - Issue #XX */ function roi_enable_gzip_compression() { // DESACTIVADO - No hacer nada // La compresión la maneja nginx, no PHP return; } // DESACTIVADO - Conflicto con W3 Total Cache page cache // add_action( 'template_redirect', 'roi_enable_gzip_compression', 0 ); /** * ============================================================================ * FUNCIONES AUXILIARES * ============================================================================ */ /** * Limpiar caché de transients expirados periódicamente * * Los transients expirados se acumulan en la base de datos. * * @since 1.0.0 */ function roi_cleanup_expired_transients() { global $wpdb; // Eliminar transients expirados (solo los del tema) $wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->options} WHERE option_name LIKE %s AND option_value < %d", $wpdb->esc_like( '_transient_timeout_roi_' ) . '%', time() ) ); } // Ejecutar limpieza semanalmente add_action( 'roi_weekly_cleanup', 'roi_cleanup_expired_transients' ); // Registrar evento cron si no existe if ( ! wp_next_scheduled( 'roi_weekly_cleanup' ) ) { wp_schedule_event( time(), 'weekly', 'roi_weekly_cleanup' ); } /* * NOTA: Funciones previamente deshabilitadas han sido reimplementadas * con mejoras para evitar loops infinitos y problemas de memoria. * * - Resource hints (dns-prefetch, preconnect) - REACTIVADO * - Preload de recursos críticos - REACTIVADO * - Optimización del Heartbeat API - REACTIVADO * - Remoción de query strings - REACTIVADO (solo para assets propios) * - Script attributes (defer/async) - REACTIVADO */