next_tag(['tag_name' => 'a'])) { $href = (string) $processor->get_attribute('href'); if ($href && preg_match('#^https?://#i', $href)) { $link_host = wp_parse_url($href, PHP_URL_HOST); if ($link_host && strcasecmp($link_host, $site_host) !== 0) { $processor->set_attribute('target', '_blank'); $rel = (string) $processor->get_attribute('rel'); $rel_parts = array_filter(array_map('trim', explode(' ', $rel))); if (!in_array('nofollow', $rel_parts, true)) { $rel_parts[] = 'nofollow'; } if (!in_array('noopener', $rel_parts, true)) { $rel_parts[] = 'noopener'; } if (!in_array('noreferrer', $rel_parts, true)) { $rel_parts[] = 'noreferrer'; } $processor->set_attribute('rel', implode(' ', array_unique($rel_parts))); } } } return $processor->get_updated_html(); } // Regex fallback return preg_replace_callback('/]+)>/i', function ($matches) use ($site_host) { $attrs = $matches[1]; if (preg_match('/href=["\'](https?:\/\/[^"\']+)["\']/i', $attrs, $url_match)) { $link_host = wp_parse_url($url_match[1], PHP_URL_HOST); if ($link_host && strcasecmp($link_host, $site_host) !== 0) { if (!preg_match('/target=["\']/i', $attrs)) { $attrs .= ' target="_blank"'; } if (preg_match('/rel=["\']([^"\']*)["\']/i', $attrs, $rel_match)) { $rel = $rel_match[1]; if (strpos($rel, 'nofollow') === false) $rel .= ' nofollow'; if (strpos($rel, 'noopener') === false) $rel .= ' noopener'; if (strpos($rel, 'noreferrer') === false) $rel .= ' noreferrer'; $attrs = preg_replace('/rel=["\'][^"\']*["\']/i', 'rel="' . trim($rel) . '"', $attrs); } else { $attrs .= ' rel="nofollow noopener noreferrer"'; } } } return ''; }, $content); } public function redirect_attachment() { add_action('template_redirect', [$this, 'redirect_attachment_page']); } public function redirect_attachment_page() { if (is_attachment()) { global $post; if ($post && !empty($post->post_parent)) { wp_safe_redirect(esc_url(get_permalink($post->post_parent)), 301); exit; } else { wp_safe_redirect(esc_url(home_url('/')), 301); exit; } } } public function redirect_single_post() { add_action('template_redirect', [$this, 'search_results_return_one_post']); } public function search_results_return_one_post() { if (is_search()) { global $wp_query; if ($wp_query && $wp_query->post_count == 1 && $wp_query->max_num_pages == 1 && !empty($wp_query->posts[0])) { wp_safe_redirect(get_permalink($wp_query->posts[0]->ID)); exit; } } } public function robots_txt() { add_filter('robots_txt', [$this, 'filter_robots_txt'], 99, 2); } public function filter_robots_txt($output, $public) { $custom = Helper::get_option('robots_txt'); if (!empty($custom)) { return $custom; } return $output; } public function slug_post_type() { add_filter('request', [$this, 'rempostslug_request_filter'], 15, 1); add_action('pre_get_posts', [$this, 'rempostslug_parse_request'], 1, 1); add_filter('post_type_link', [$this, 'rempostslug_fun'], 10, 3); add_filter('get_the_permalink', [$this, 'rempostslug_fun'], 10, 3); add_filter('the_permalink', [$this, 'rempostslug_fun'], 10, 3); } public function slug_taxonomy() { add_filter('request', [$this, 'remtaxslug_change_term_request'], 1, 1); add_filter('term_link', [$this, 'remtaxslug_term_permalink'], 10, 2); add_filter('get_category_link', [$this, 'remtaxslug_term_permalink'], 10, 3); add_filter('get_term_link', [$this, 'remtaxslug_term_permalink'], 10, 3); add_filter('category_link', [$this, 'remtaxslug_term_permalink'], 10, 3); } public function rempostslug_fun($post_link, $post) { $post_type_list = (array) Helper::get_option('slug_post_type', []); if (empty($post_type_list) || !($post instanceof \WP_Post)) { return $post_link; } $post_type = get_post_type($post); if (in_array($post_type, $post_type_list, true)) { $uri = is_post_type_hierarchical($post_type) ? get_page_uri($post) : $post->post_name; $post_link = home_url(user_trailingslashit($uri)); } return $post_link; } public function rempostslug_request_filter($query_vars) { if (is_admin()) { return $query_vars; } $post_type_list = (array) Helper::get_option('slug_post_type', []); if (empty($post_type_list)) { return $query_vars; } $uri_path = ''; if (!empty($query_vars['name'])) { $uri_path = $query_vars['name']; } elseif (!empty($query_vars['pagename'])) { $uri_path = $query_vars['pagename']; } elseif (!empty($query_vars['category_name']) && !empty($query_vars['name'])) { $uri_path = $query_vars['category_name'] . '/' . $query_vars['name']; } else { $uri = strtok($_SERVER['REQUEST_URI'] ?? '', '?'); $uri_path = trim((string) $uri, '/'); } if (!empty($uri_path)) { $existing_page = get_page_by_path($uri_path, OBJECT, 'page'); if ($existing_page && 'publish' === $existing_page->post_status) { return $query_vars; } $found_post = get_page_by_path($uri_path, OBJECT, $post_type_list); if ($found_post && 'publish' === $found_post->post_status) { unset($query_vars['error'], $query_vars['pagename'], $query_vars['category_name'], $query_vars['category']); $query_vars['name'] = $found_post->post_name; $query_vars['post_type'] = $found_post->post_type; $query_vars[$found_post->post_type] = $uri_path; return $query_vars; } if (strpos($uri_path, '/') === false) { global $wpdb; $escaped_types = "'" . implode("','", array_map('esc_sql', $post_type_list)) . "'"; $found_post_type = $wpdb->get_var($wpdb->prepare( "SELECT post_type FROM {$wpdb->posts} WHERE post_name = %s AND post_type IN ({$escaped_types}) AND post_status = 'publish' LIMIT 1", $uri_path )); if ($found_post_type) { unset($query_vars['error'], $query_vars['pagename'], $query_vars['category_name'], $query_vars['category']); $query_vars['name'] = $uri_path; $query_vars['post_type'] = $found_post_type; $query_vars[$found_post_type] = $uri_path; } } } return $query_vars; } public function rempostslug_parse_request($query) { if (!$query->is_main_query() || is_admin()) { return; } $post_type_list = (array) Helper::get_option('slug_post_type', []); if (empty($post_type_list)) { return; } $name = $query->get('name'); if (!empty($name)) { $current_post_type = $query->get('post_type'); if (empty($current_post_type) || $current_post_type === 'post') { $query->set('post_type', array_unique(array_merge(['post', 'page'], $post_type_list))); } } } public function slug_category_post() { add_filter('post_link', [$this, 'filter_category_post_link'], 10, 2); } public function filter_category_post_link($permalink, $post) { if (!($post instanceof \WP_Post) || $post->post_type !== 'post') { return $permalink; } $cats = get_the_category($post->ID); if (!empty($cats) && !is_wp_error($cats)) { $cat_slug = $cats[0]->slug; return home_url('/' . $cat_slug . '/' . $post->post_name . '/'); } return $permalink; } public function slug_hierarchical_page() { add_filter('request', [$this, 'resolve_hierarchical_page_request'], 20); } public function resolve_hierarchical_page_request($query_vars) { if (is_admin()) { return $query_vars; } $uri_path = ''; if (!empty($query_vars['category_name']) && !empty($query_vars['name'])) { $uri_path = $query_vars['category_name'] . '/' . $query_vars['name']; } elseif (!empty($query_vars['pagename'])) { $uri_path = $query_vars['pagename']; } else { $uri = strtok($_SERVER['REQUEST_URI'] ?? '', '?'); $uri_path = trim((string) $uri, '/'); } if (!empty($uri_path)) { $page = get_page_by_path($uri_path, OBJECT, 'page'); if ($page && 'publish' === $page->post_status) { unset($query_vars['error'], $query_vars['category_name'], $query_vars['name'], $query_vars['category']); $query_vars['pagename'] = $uri_path; $query_vars['post_type'] = 'page'; return $query_vars; } $slug = $query_vars['name'] ?? $query_vars['category_name'] ?? (strpos($uri_path, '/') === false ? $uri_path : ''); if (!empty($slug)) { $blog_page = get_page_by_path('blog/' . $slug, OBJECT, 'page'); if ($blog_page && 'publish' === $blog_page->post_status) { unset($query_vars['error'], $query_vars['name'], $query_vars['category_name'], $query_vars['category']); $query_vars['pagename'] = 'blog/' . $slug; $query_vars['post_type'] = 'page'; return $query_vars; } } } return $query_vars; } public function remtaxslug_change_term_request($query) { $tax_names = (array) Helper::get_option('slug_taxonomy', []); foreach ($tax_names as $current_tax_name) { if (array_key_exists('attachment', $query) && $query['attachment']) { $include_children = true; $name = $query['attachment']; } else { $include_children = false; $name = array_key_exists('name', $query) ? $query['name'] : ''; } $term = get_term_by('slug', $name, $current_tax_name); if (!empty($name) && !empty($term) && !is_wp_error($term)) { if ($include_children) { unset($query['attachment']); $parent = $term->parent; while ($parent) { $parent_term = get_term($parent, $current_tax_name); if ($parent_term && !is_wp_error($parent_term)) { $name = $parent_term->slug . '/' . $name; $parent = $parent_term->parent; } else { break; } } } else { unset($query['name']); } switch ($current_tax_name) { case 'category': $query['category_name'] = $name; break; case 'post_tag': $query['tag'] = $name; break; default: $query[$current_tax_name] = $name; break; } } } return $query; } public function remtaxslug_term_permalink($url, $taxonomy) { if (is_int($taxonomy)) { $taxonomy_term = get_term($taxonomy); if (!($taxonomy_term instanceof \WP_Term)) { return $url; } $taxonomy_slug = $taxonomy_term->slug; $taxonomy_name = $taxonomy_term->taxonomy; } elseif (is_object($taxonomy) && isset($taxonomy->slug, $taxonomy->taxonomy)) { $taxonomy_slug = $taxonomy->slug; $taxonomy_name = $taxonomy->taxonomy; } else { return $url; } $saved_value = (array) Helper::get_option('slug_taxonomy', []); if (empty($saved_value)) { return $url; } if (in_array($taxonomy_name, $saved_value, true)) { $url = trailingslashit(get_option('home')) . user_trailingslashit($taxonomy_slug); } return $url; } }