167 lines
5.3 KiB
PHP
167 lines
5.3 KiB
PHP
<?php
|
|
/**
|
|
* AKSRT Category Rewrite — Remove /category/ from URLs
|
|
*
|
|
* Rewrites category URLs from /category/slug/ to /slug/.
|
|
* Adds 301 redirect for old /category/ URLs.
|
|
*
|
|
* WARNING: May conflict with other "no-category" plugins.
|
|
* Enable via Settings → SEO → Remove category base.
|
|
*
|
|
* @package AKSRT
|
|
*/
|
|
|
|
defined('ABSPATH') || exit;
|
|
|
|
class AKSRT_Category_Rewrite {
|
|
|
|
private static $instance = null;
|
|
|
|
private function __construct() {}
|
|
|
|
private function rewrite_version(): string {
|
|
return AKSRT_VERSION . ':category-rewrite-v1';
|
|
}
|
|
|
|
public static function get_instance(): self {
|
|
if (null === self::$instance) {
|
|
self::$instance = new self();
|
|
}
|
|
return self::$instance;
|
|
}
|
|
|
|
public function init(): void {
|
|
if (!$this->is_enabled()) {
|
|
// If disabled, ensure old rules are cleaned up
|
|
add_action('init', [$this, 'maybe_restore']);
|
|
return;
|
|
}
|
|
|
|
add_filter('category_link', [$this, 'remove_category_base'], 10, 2);
|
|
add_filter('term_link', [$this, 'remove_category_base_term'], 10, 3);
|
|
add_action('init', [$this, 'add_rewrite_rules']);
|
|
add_action('template_redirect', [$this, 'redirect_old_urls']);
|
|
add_action('admin_init', [$this, 'maybe_flush']);
|
|
}
|
|
|
|
/**
|
|
* Check if feature is enabled
|
|
*/
|
|
private function is_enabled(): bool {
|
|
if (!class_exists('AKSRT_Settings')) return false;
|
|
// schema key = seo_no_category; runtime alias = seo_remove_category
|
|
return (bool) (
|
|
AKSRT_Settings::get_value('seo_remove_category', false)
|
|
?: AKSRT_Settings::get_value('seo_no_category', false)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Remove /category/ from category link
|
|
*/
|
|
public function remove_category_base(string $link, $term): string {
|
|
$base = get_option('category_base');
|
|
if ($base && $base !== 'category') return $link;
|
|
|
|
// Only process category taxonomy
|
|
if ($term instanceof WP_Term && $term->taxonomy !== 'category') {
|
|
return $link;
|
|
}
|
|
|
|
$category_base_url = preg_quote(trailingslashit(home_url('/category')), '#');
|
|
return preg_replace('#' . $category_base_url . '#', trailingslashit(home_url('/')), $link, 1);
|
|
}
|
|
|
|
/**
|
|
* Remove base from term_link for categories
|
|
*/
|
|
public function remove_category_base_term(string $link, $term, $taxonomy): string {
|
|
if ($taxonomy !== 'category') return $link;
|
|
return $this->remove_category_base($link, $term);
|
|
}
|
|
|
|
/**
|
|
* Add rewrite rules for category URLs without /category/
|
|
*/
|
|
public function add_rewrite_rules(): void {
|
|
// Get all category slugs
|
|
$categories = get_categories(['hide_empty' => false]);
|
|
foreach ($categories as $cat) {
|
|
if (empty($cat->slug)) continue;
|
|
$slug = $cat->slug;
|
|
|
|
// /slug/ → index.php?category_name=$slug
|
|
add_rewrite_rule(
|
|
'^' . preg_quote($slug, '/') . '/?$',
|
|
'index.php?category_name=' . $slug,
|
|
'top'
|
|
);
|
|
|
|
// /slug/page/N/ → index.php?category_name=$slug&paged=$N
|
|
add_rewrite_rule(
|
|
'^' . preg_quote($slug, '/') . '/page/([0-9]+)/?$',
|
|
'index.php?category_name=$slug&paged=$matches[1]',
|
|
'top'
|
|
);
|
|
|
|
// /slug/feed/ → index.php?category_name=$slug&feed=$feed
|
|
add_rewrite_rule(
|
|
'^' . preg_quote($slug, '/') . '/feed/?$',
|
|
'index.php?category_name=$slug&feed=rss2',
|
|
'top'
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 301 redirect old /category/ URLs to new ones
|
|
*/
|
|
public function redirect_old_urls(): void {
|
|
$request = $_SERVER['REQUEST_URI'] ?? '';
|
|
$home_path = wp_parse_url(home_url('/'), PHP_URL_PATH);
|
|
|
|
// Remove home path prefix
|
|
if ($home_path && strpos($request, $home_path) === 0) {
|
|
$request = substr($request, strlen($home_path));
|
|
}
|
|
|
|
// Check if request starts with /category/
|
|
if (preg_match('#^category/(.+?)/?$#', $request, $matches)) {
|
|
$new_url = home_url('/' . $matches[1] . '/');
|
|
wp_redirect($new_url, 301);
|
|
exit;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Flush rewrite rules when setting changes
|
|
*/
|
|
public function maybe_flush(): void {
|
|
$rewrite_version = $this->rewrite_version();
|
|
$stored = (string) get_option('aksrt_category_rewrite_flushed', '');
|
|
if ($stored !== $rewrite_version) {
|
|
flush_rewrite_rules();
|
|
update_option('aksrt_category_rewrite_flushed', $rewrite_version);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Restore default rewrite rules when feature is disabled
|
|
*/
|
|
public function maybe_restore(): void {
|
|
$stored = (string) get_option('aksrt_category_rewrite_flushed', '');
|
|
if ($stored !== '') {
|
|
flush_rewrite_rules();
|
|
delete_option('aksrt_category_rewrite_flushed');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Force flush (call after enabling/disabling)
|
|
*/
|
|
public static function force_flush(): void {
|
|
flush_rewrite_rules();
|
|
update_option('aksrt_category_rewrite_flushed', AKSRT_VERSION . ':category-rewrite-v1');
|
|
}
|
|
}
|