افزونه مینی سئو
با افزونه مکمل سئو «مینی سئو»، تحلیلهای هوشمند و پیشنهادهای کاربردی را مستقیماً در صفحه ویرایش نوشته خود دریافت کنید و با اطمینان محتوایی تولید کنید که گوگل عاشق آن میشود.
دستیار هوشمند و مکمل برای افزونههای Rank Math و Yoast.
-
شکارچی جایگاه ویژه گوگل 🎯
با تحلیلگر AEO، مستقیم به بخش پاسخهای ویژه (Featured Snippets) بروید. -
پادشاه نتایج محلی شوید 👑
تحلیلگر GEO برای جذب مشتریان از شهر و منطقه خودتان. -
دو متخصص در یک افزونه ✌️
بهینهسازی همزمان برای پاسخگویی (AEO) و جستجوی محلی (GEO). -
چکلیست کاربردی، نه تئوری 📝
پیشنهادهای عملی و دقیق برای رفع سریع ایرادات سئوی محتوا. -
امتیازدهی آنی و هوشمند 📊
- سازگار با ووکامرس 🛒
- قیمت ۲۹۰/۰۰۰ تومان
- + نسخه رایگان تستی😊
- کدهارا درون افزونه wp code بذارید
- یک اسنیپت جدید PHPبسازید
- کدها راکپی پیست کنید.
/**
* Plugin Name: مینی سئو
* Plugin URI: https:/mrbarati.com/
* Description: تحلیلگر Aio و GEO، سازگار با Rank Math و Yoast SEO. مناسب برای نوشته، برگه و ووکامرس.
* Version: 1.0.0
* Author: براتی طراح سایت
* Author URI: https://mrbarati.com/
* License: GPL-2.0-or-later
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* اگر کلاس از قبل وجود دارد، دوباره تعریف نکن — مناسب برای WP Code / snippets
*/
if ( ! class_exists( 'Mini_SEO_Analyzor' ) ) :
final class Mini_SEO_Analyzor {
/**
* Singleton instance
* @var Mini_SEO_Analyzor|null
*/
private static $instance = null;
/**
* دریافت نمونه
*/
public static function instance() {
if ( self::$instance === null ) {
self::$instance = new self();
}
return self::$instance;
}
/**
* سازنده: ثبت هوکها
*/
private function __construct() {
add_action( 'add_meta_boxes', [ $this, 'register_meta_box' ] );
add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_assets' ] );
add_action( 'wp_ajax_mini_seo_analyzor_run_analysis', [ $this, 'ajax_run_analysis' ] );
add_action( 'wp_ajax_mini_seo_analyzor_toggle_temp_disable', [ $this, 'ajax_toggle_temp_disable' ] );
add_action( 'save_post', [ $this, 'save_meta_box' ], 10, 2 );
add_action( 'admin_notices', [ $this, 'check_seo_plugins_notice' ] );
}
/* ========================================================
* رندر متاباکس
* ======================================================== */
public function register_meta_box(): void {
$post_types = [ 'post', 'page', 'product' ];
add_meta_box(
'mini-seo-analyzor-box',
__( 'تحلیلگر Aio و GEO', 'mini-seo-analyzor' ),
[ $this, 'render_meta_box' ],
$post_types,
'normal',
'high'
);
}
public function render_meta_box( $post ): void {
$temp_disable = get_post_meta( $post->ID, '_mini_seo_analyzor_temp_disable', true );
$temp_disable = $temp_disable ? 1 : 0;
$meta_nonce = wp_create_nonce( 'mini_seo_analyzor_meta_nonce' );
$provinces_json = wp_json_encode( $this->get_provinces_list() );
?>
<div class="mini-seo-analyzor-wrapper"
data-post-id="<?php echo esc_attr( $post->ID ); ?>"
data-temp-disable="<?php echo esc_attr( $temp_disable ); ?>"
data-provinces="<?php echo esc_attr( $provinces_json ); ?>">
<div class="mini-seo-tabs">
<a href="#aio-panel" class="mini-seo-tab active" data-tab="aio-panel"><?php esc_html_e( 'تحلیل Aio', 'mini-seo-analyzor' ); ?></a>
<a href="#geo-panel" class="mini-seo-tab" data-tab="geo-panel"><?php esc_html_e( 'تحلیل GEO', 'mini-seo-analyzor' ); ?></a>
<a href="#aio-tutorial-panel" class="mini-seo-tab" data-tab="aio-tutorial-panel"><?php esc_html_e( 'راهنمای Aio', 'mini-seo-analyzor' ); ?></a>
<a href="#geo-tutorial-panel" class="mini-seo-tab" data-tab="geo-tutorial-panel"><?php esc_html_e( 'راهنمای GEO', 'mini-seo-analyzor' ); ?></a>
</div>
<div id="aio-panel" class="mini-seo-panel active">
<div class="analysis-results"><div class="loading-message"><?php esc_html_e( 'برای شروع تحلیل، محتوا را ویرایش کنید...', 'mini-seo-analyzor' ); ?></div></div>
</div>
<div id="geo-panel" class="mini-seo-panel">
<div class="analysis-results"><div class="loading-message"><?php esc_html_e( 'در حال بارگذاری...', 'mini-seo-analyzor' ); ?></div></div>
</div>
<div id="aio-tutorial-panel" class="mini-seo-panel mini-seo-tutorial">
<?php echo $this->get_aio_tutorial_content(); ?>
</div>
<div id="geo-tutorial-panel" class="mini-seo-panel mini-seo-tutorial">
<?php echo $this->get_geo_tutorial_content(); ?>
</div>
<div class="mini-seo-controls" style="margin-top:12px;padding:12px;border:1px solid #e6e6e6;background:#fff;display:flex;align-items:center;gap:12px;">
<label style="display:flex;align-items:center;gap:10px;cursor:pointer;margin:0;">
<input type="checkbox" id="mini_seo_analyzor_temp_disable" <?php checked( $temp_disable, 1 ); ?> />
<span style="font-weight:600;"><?php esc_html_e( 'غیر فعال سازی موقت', 'mini-seo-analyzor' ); ?></span>
</label>
<span id="mini_seo_analyzor_status_badge" style="display:inline-block;padding:6px 10px;border-radius:14px;font-weight:700;color:#fff;font-size:13px;">
<?php if ( $temp_disable ) : ?>
<span style="background:#c82333;padding:6px 10px;border-radius:14px;display:inline-block;"><?php esc_html_e( 'غیرفعال', 'mini-seo-analyzor' ); ?></span>
<?php else : ?>
<span style="background:#198754;padding:6px 10px;border-radius:14px;display:inline-block;"><?php esc_html_e( 'فعال', 'mini-seo-analyzor' ); ?></span>
<?php endif; ?>
</span>
<span style="color:#666;font-size:13px;margin-left:auto;"><?php esc_html_e( '(اگر تیک زده شود، آنالیز خودکار متوقف میشود)', 'mini-seo-analyzor' ); ?></span>
<input type="hidden" id="mini_seo_analyzor_meta_nonce" value="<?php echo esc_attr( $meta_nonce ); ?>" />
</div>
</div>
<?php
}
/* ========================================================
* enqueue CSS/JS
* ======================================================== */
public function enqueue_assets( string $hook ): void {
$screen = function_exists('get_current_screen') ? get_current_screen() : null;
if ( ! $screen || ! in_array( $screen->post_type ?? '', [ 'post', 'page', 'product' ], true ) || ! in_array( $hook, [ 'post.php', 'post-new.php' ], true ) ) {
return;
}
$css = ".mini-seo-tabs{display:flex;flex-wrap:wrap;border-bottom:2px solid #007cba;margin-bottom:0}.mini-seo-tab{padding:12px 18px;cursor:pointer;background:#f0f0f1;border:1px solid #ccc;border-bottom:none;margin-left:5px;font-weight:600;border-radius:8px 8px 0 0;position:relative;top:2px;transition:all .2s ease-in-out;text-decoration:none;color:#3c434a}.mini-seo-tab:hover{background:#e0e0e0}.mini-seo-tab.active{background:#007cba;color:#fff;border-color:#007cba}.mini-seo-panel{display:none;padding:25px;border:1px solid #ccc;border-top:none;background:#fff;clear:both}.mini-seo-panel.active{display:block}.mini-seo-panel .loading-message{padding:40px;text-align:center;color:#50575e;font-size:14px}.mini-seo-progress-bar{background-color:#e9ecef;border-radius:5px;overflow:hidden;margin:10px 0 25px;height:22px;direction:ltr;border:1px solid #ddd}.mini-seo-progress-bar-fill{height:100%;text-align:center;color:#fff;font-weight:700;font-size:13px;line-height:22px;transition:width .5s ease-in-out}.mini-seo-checklist{list-style:none;padding:0;margin:0}.mini-seo-checklist li{margin-bottom:14px;font-size:14px;padding-right:15px;border-right:4px solid;line-height:1.6}.mini-seo-checklist li.status-pass{border-color:#28a745}.mini-seo-checklist li.status-fail{border-color:#dc3545}.mini-seo-suggestion{font-size:13px;color:#50575e;margin:-8px 19px 15px 0;background:#f8f9fa;padding:10px;border-radius:4px;border:1px solid #e9ecef}.mini-seo-suggestion a{font-weight:700;text-decoration:none}.mini-seo-tutorial h4{font-size:16px;font-weight:700;border-bottom:2px solid #007cba;padding-bottom:5px;margin-top:25px;margin-bottom:15px}.mini-seo-tutorial p,.mini-seo-tutorial ul li{font-size:14px;line-height:1.8;color:#333}.mini-seo-tutorial code{background:#fffdf7;padding:3px 6px;border-radius:4px;font-size:15px;color:#b50000;border:1px solid #eee8d5;font-family:monospace}.mini-seo-tutorial strong{color:#005a87}";
wp_register_style( 'mini-seo-analyzor-style', false, [], '1.2.0' );
wp_enqueue_style( 'mini-seo-analyzor-style' );
wp_add_inline_style( 'mini-seo-analyzor-style', $css );
wp_register_script( 'mini-seo-analyzor-script', false, [ 'jquery' ], '1.2.0', true );
wp_enqueue_script( 'mini-seo-analyzor-script' );
$vars = [
'ajax_url' => admin_url( 'admin-ajax.php' ),
'ajax_nonce' => wp_create_nonce( 'mini_seo_analyzor_ajax_nonce' ),
];
wp_localize_script( 'mini-seo-analyzor-script', 'mini_seo_analyzor_vars', $vars );
$js = <<<JS
jQuery(document).ready(function($) {
$('.mini-seo-tab').on('click', function(e) {
e.preventDefault();
const tabId = $(this).data('tab');
$('.mini-seo-tab, .mini-seo-panel').removeClass('active');
$(this).addClass('active');
$('#' + tabId).addClass('active');
});
let analysisTimeout = null;
const wrapper = $('.mini-seo-analyzor-wrapper');
const postId = parseInt(wrapper.data('post-id')) || 0;
let tempDisable = wrapper.data('temp-disable') === 1 || wrapper.data('temp-disable') === '1';
function updateStatusBadge(state) {
const badgeWrap = $('#mini_seo_analyzor_status_badge');
badgeWrap.empty();
if ( state ) {
badgeWrap.append('<span style="background:#c82333;padding:6px 10px;border-radius:14px;display:inline-block;color:#fff;">غیرفعال</span>');
} else {
badgeWrap.append('<span style="background:#198754;padding:6px 10px;border-radius:14px;display:inline-block;color:#fff;">فعال</span>');
}
}
function setTempDisableUI(state, options = {}) {
tempDisable = !!state;
wrapper.attr('data-temp-disable', tempDisable ? '1' : '0');
$('#mini_seo_analyzor_temp_disable').prop('checked', tempDisable);
updateStatusBadge(tempDisable);
if (tempDisable) {
$('#aio-panel .analysis-results, #geo-panel .analysis-results').html('<div class="loading-message">تحلیل موقتاً غیرفعال است.</div>');
} else {
if ( ! options.suppressPerform ) {
performAnalysis();
}
}
}
updateStatusBadge(tempDisable);
function collectEditorData() {
let postTitle = '', postContent = '', metaDesc = '', focusKeyword = '';
if (typeof wp !== 'undefined' && wp.data && wp.data.select) {
try {
const editor = wp.data.select('core/editor');
if ( editor && editor.getEditedPostAttribute ) {
postTitle = editor.getEditedPostAttribute('title') || '';
postContent = editor.getEditedPostAttribute('content') || '';
}
if ( wp.data.select('rank-math-analyzer') ) {
const rankMathData = wp.data.select('rank-math-analyzer');
metaDesc = rankMathData.getMetaDescription ? rankMathData.getMetaDescription() : metaDesc;
try { focusKeyword = rankMathData.getKeyword('primary')?.value || focusKeyword; } catch(e){}
} else if ( wp.data.select('yoast-seo/editor') ) {
const yoast = wp.data.select('yoast-seo/editor');
metaDesc = yoast.getSeoMeta ? (yoast.getSeoMeta()?.metadesc || '') : metaDesc;
focusKeyword = yoast.getSeoMeta ? (yoast.getSeoMeta()?.focuskw || '') : focusKeyword;
}
} catch (ex) {}
}
if (!postTitle) postTitle = $('#title').val() || '';
if (!postContent) postContent = (typeof tinymce !== 'undefined' && tinymce.get('content')) ? tinymce.get('content').getContent() : $('#content').val() || '';
if (!metaDesc) metaDesc = $('#yoast_wpseo_metadesc').val() || $('textarea[name="rank_math_description"]').val() || '';
if (!focusKeyword) focusKeyword = $('#yoast_wpseo_focuskw').val() || $('input[name="rank_math_focus_keyword"]').val() || '';
return { postTitle, postContent, metaDesc, focusKeyword };
}
function performAnalysis() {
if ( tempDisable ) return;
const loadingHtml = '<div class="loading-message">در حال تحلیل... لطفاً صبر کنید.</div>';
$('#aio-panel .analysis-results, #geo-panel .analysis-results').html(loadingHtml);
if (!postId) return;
const data = collectEditorData();
$.post(mini_seo_analyzor_vars.ajax_url, {
action: 'mini_seo_analyzor_run_analysis',
nonce: mini_seo_analyzor_vars.ajax_nonce,
post_id: postId,
title: data.postTitle,
content: data.postContent,
meta_description: data.metaDesc,
focus_keyword: data.focusKeyword
}, function(response) {
if ( response && response.success ) {
$('#aio-panel .analysis-results').html(response.data.aio_html);
$('#geo-panel .analysis-results').html(response.data.geo_html);
} else {
const err = (response && response.data && response.data.message) ? response.data.message : 'یک خطای نامشخص رخ داد.';
$('#aio-panel .analysis-results, #geo-panel .analysis-results').html('<div class="loading-message" style="color:red;">' + err + '</div>');
}
}).fail(function() {
$('#aio-panel .analysis-results, #geo-panel .analysis-results').html('<div class="loading-message" style="color:red;">خطا در ارتباط با سرور.</div>');
});
}
const debounceAnalysis = () => { clearTimeout(analysisTimeout); analysisTimeout = setTimeout(performAnalysis, 1500); };
if ( typeof wp !== 'undefined' && wp.data && wp.data.subscribe ) {
wp.data.subscribe(debounceAnalysis);
} else {
$('#title, #content').on('input keyup', debounceAnalysis);
if ( typeof tinymce !== 'undefined' && tinymce.get('content') ) {
tinymce.get('content').on('keyup input', debounceAnalysis);
}
}
setTimeout(function() {
if ( ! tempDisable ) performAnalysis();
}, ۲۵۰۰);
$('#mini_seo_analyzor_temp_disable').on('change', function() {
const checked = $(this).is(':checked') ? 1 : 0;
const prev = tempDisable ? 1 : 0;
setTempDisableUI(checked, { suppressPerform: true });
$('#mini_seo_analyzor_temp_disable').prop('disabled', true);
$.post(mini_seo_analyzor_vars.ajax_url, {
action: 'mini_seo_analyzor_toggle_temp_disable',
nonce: $('#mini_seo_analyzor_meta_nonce').val(),
post_id: postId,
value: checked
}, function(res) {
if ( res && res.success && typeof res.data.value !== 'undefined' ) {
const saved = parseInt(res.data.value, 10) ? 1 : 0;
setTempDisableUI(saved, { suppressPerform: true });
if ( saved === 0 ) {
performAnalysis();
}
} else {
setTempDisableUI(prev, { suppressPerform: true });
alert('خطا در دریافت پاسخ سرور. وضعیت بازگردانده شد.');
}
}).fail(function() {
setTempDisableUI(prev, { suppressPerform: true });
alert('خطا در ذخیرهسازی وضعیت. دسترسی یا nonce بررسی شود.');
}).always(function() {
$('#mini_seo_analyzor_temp_disable').prop('disabled', false);
});
});
});
JS;
wp_add_inline_script( 'mini-seo-analyzor-script', $js );
}
/* ========================================================
* AJAX handlers
* ======================================================== */
public function ajax_run_analysis(): void {
if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'mini_seo_analyzor_ajax_nonce' ) ) {
wp_send_json_error( [ 'message' => 'Nonce نامعتبر.' ], ۴۰۰ );
}
$post_id = isset( $_POST['post_id'] ) ? absint( wp_unslash( $_POST['post_id'] ) ) : 0;
if ( ! $post_id || ! current_user_can( 'edit_post', $post_id ) ) {
wp_send_json_error( [ 'message' => 'دسترسی غیرمجاز.' ], ۴۰۳ );
}
$title = isset( $_POST['title'] ) ? sanitize_text_field( wp_unslash( $_POST['title'] ) ) : '';
$content_html = isset( $_POST['content'] ) ? wp_kses_post( wp_unslash( $_POST['content'] ) ) : '';
$meta_desc = isset( $_POST['meta_description'] ) ? sanitize_text_field( wp_unslash( $_POST['meta_description'] ) ) : '';
$focus_keyword = isset( $_POST['focus_keyword'] ) ? sanitize_text_field( wp_unslash( $_POST['focus_keyword'] ) ) : '';
$content_text = wp_strip_all_tags( $content_html );
if ( empty( $focus_keyword ) ) $focus_keyword = $this->get_focus_keyword_from_meta( $post_id );
if ( empty( $meta_desc ) ) $meta_desc = $this->get_meta_description_from_meta( $post_id );
if ( empty( $focus_keyword ) ) {
wp_send_json_error( [ 'message' => 'کلمه کلیدی اصلی تنظیم نشده است! لطفاً ابتدا کلمه کلیدی را در باکس افزونه Rank Math یا Yoast SEO وارد کنید.' ] );
}
if ( get_post_meta( $post_id, '_mini_seo_analyzor_temp_disable', true ) ) {
wp_send_json_error( [ 'message' => 'تحلیل به صورت موقت توسط کاربر غیرفعال شده است.' ] );
}
$schema_info = $this->get_active_schema_info( $post_id );
$aio_html = $this->analyze_aio( $post_id, $title, $content_html, $content_text, $meta_desc, $focus_keyword, $schema_info );
$geo_html = $this->analyze_geo( $post_id, $content_html, $content_text, $title, $meta_desc, $schema_info );
wp_send_json_success( [ 'aio_html' => $aio_html, 'geo_html' => $geo_html ] );
}
public function ajax_toggle_temp_disable(): void {
if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'mini_seo_analyzor_meta_nonce' ) ) {
wp_send_json_error( [ 'message' => 'Nonce نامعتبر.' ], ۴۰۰ );
}
$post_id = isset( $_POST['post_id'] ) ? absint( wp_unslash( $_POST['post_id'] ) ) : 0;
if ( ! $post_id || ! current_user_can( 'edit_post', $post_id ) ) {
wp_send_json_error( [ 'message' => 'دسترسی غیرمجاز.' ], ۴۰۳ );
}
$value = isset( $_POST['value'] ) ? intval( wp_unslash( $_POST['value'] ) ) : 0;
$value = $value ? 1 : 0;
if ( $value ) {
update_post_meta( $post_id, '_mini_seo_analyzor_temp_disable', 1 );
} else {
delete_post_meta( $post_id, '_mini_seo_analyzor_temp_disable' );
}
if ( function_exists( 'clean_post_cache' ) ) {
clean_post_cache( $post_id );
}
wp_send_json_success( [ 'saved' => true, 'value' => $value ] );
}
/* ========================================================
* ذخیره هنگام save_post (fallback)
* ======================================================== */
public function save_meta_box( $post_id, $post ) {
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) return;
if ( ! current_user_can( 'edit_post', $post_id ) ) return;
if ( isset( $_POST['mini_seo_analyzor_meta_nonce'] ) ) {
$nonce = sanitize_text_field( wp_unslash( $_POST['mini_seo_analyzor_meta_nonce'] ) );
if ( ! wp_verify_nonce( $nonce, 'mini_seo_analyzor_meta_nonce' ) ) return;
}
if ( isset( $_POST['mini_seo_analyzor_temp_disable'] ) ) {
$val = sanitize_text_field( wp_unslash( $_POST['mini_seo_analyzor_temp_disable'] ) );
if ( $val ) {
update_post_meta( $post_id, '_mini_seo_analyzor_temp_disable', 1 );
} else {
delete_post_meta( $post_id, '_mini_seo_analyzor_temp_disable' );
}
}
}
/* ========================================================
* نوتیفیکیشن اگر Rank Math و Yoast نصب نبودند
* ======================================================== */
public function check_seo_plugins_notice() {
if ( ! is_admin() || ! current_user_can( 'edit_posts' ) ) return;
$screen = function_exists('get_current_screen') ? get_current_screen() : null;
if ( ! $screen || ! in_array( $screen->base ?? '', [ 'post', 'post-new' ], true ) ) return;
if ( ! function_exists( 'is_plugin_active' ) ) {
include_once ABSPATH . 'wp-admin/includes/plugin.php';
}
if ( ! is_plugin_active( 'seo-by-rank-math/rank-math.php' ) && ! is_plugin_active( 'wordpress-seo/wp-seo.php' ) ) {
echo '<div class="notice notice-warning is-dismissible"><p>⚠️ هیچ پلاگین سئوی اصلی (Rank Math یا Yoast SEO) فعال نیست. برخی از دادههای آنالیز Aio/GEO ممکن است کامل نباشند.</p></div>';
}
}
/* ========================================================
* توابع تحلیل (Aio / GEO)
* ======================================================== */
private function analyze_aio( int $post_id, string $title, string $content_html, string $content_text, string $meta_desc, string $keyword, array $schema_info ): string {
$score = 0;
$checks = [];
// کلمه کلیدی در عنوان: ۱۵ امتیاز
if ( $this->keyword_exists_as_whole_word( $title, $keyword ) ) $score += 15;
$checks[] = ['label' => 'کلمه کلیدی در عنوان', 'status' => $this->keyword_exists_as_whole_word( $title, $keyword ), 'suggestion' => 'کلمه کلیدی اصلی باید در عنوان صفحه وجود داشته باشد. بهتر است در ابتدای عنوان بیاید.'];
// کلمه کلیدی در توضیحات متا: ۱۰ امتیاز
if ( $this->keyword_exists_as_whole_word( $meta_desc, $keyword ) ) $score += 10;
$checks[] = ['label' => 'کلمه کلیدی در توضیحات متا', 'status' => $this->keyword_exists_as_whole_word( $meta_desc, $keyword ), 'suggestion' => 'استفاده از کلمه کلیدی در توضیحات متا (Meta Description) برای افزایش نرخ کلیک در نتایج گوگل ضروری است.'];
// وضعیت ایندکس: ۱۵ امتیاز
$is_indexable = $this->is_post_indexable( $post_id );
if ( $is_indexable ) $score += 15;
$checks[] = ['label' => 'وضعیت ایندکس: ' . ( $is_indexable ? 'ایندکس (Index)' : 'نوایندکس (NoIndex)' ), 'status' => $is_indexable, 'suggestion' => 'برای دیده شدن در گوگل، این صفحه باید قابل ایندکس باشد. تیک No Index را از تنظیمات سئو بردارید.'];
// استفاده از H2 با کلمه کلیدی: ۱۰ امتیاز
$has_h2_with_kw = (bool) preg_match( '/<h2[^>]*>.*' . preg_quote( $keyword, '/' ) . '.*<\/h2>/isu', $content_html );
if ( $has_h2_with_kw ) $score += 10;
$checks[] = ['label' => 'استفاده از تگ H2 با کلمه کلیدی', 'status' => $has_h2_with_kw, 'suggestion' => 'کلمه کلیدی اصلی را در حداقل یکی از زیرعنوانهای H2 به کار ببرید تا ساختار محتوا برای گوگل واضحتر شود.'];
// پیوندها: مجموعاً ۲۰ امتیاز (۱۰ برای داخلی، ۱۰ برای خارجی)
$link_counts = $this->analyze_links_in_content( $content_html );
$total_links = $link_counts['internal'] + $link_counts['external'];
if ( $link_counts['internal'] > 0 ) $score += 10;
if ( $link_counts['external'] > 0 ) $score += 10;
$link_status = $total_links > 0;
$link_label = "پیوندها: {$total_links} | خارجی: {$link_counts['external']} | داخلی: {$link_counts['internal']}";
$suggestion = '';
if ( ! $link_status ) { // فقط اگر هیچ لینکی نباشد، این پیشنهاد نمایش داده میشود
$suggestion = 'محتوای شما هیچ پیوندی ندارد. برای بهبود سئو، به صفحات دیگر سایت خود (لینک داخلی) و به سایتهای معتبر خارجی (لینک خارجی) ارجاع دهید.';
} elseif ( $link_counts['internal'] === 0 ) { // اگر لینک خارجی هست ولی داخلی نیست
$suggestion = 'هیچ لینک داخلی یافت نشد. با لینک دادن به سایر نوشتههای مرتبط در سایت خود، به کاربران و گوگل در یافتن محتوای بیشتر کمک کنید.';
} elseif ( $link_counts['external'] === 0 ) { // اگر لینک داخلی هست ولی خارجی نیست
$suggestion = 'هیچ لینک خارجی یافت نشد. ارجاع دادن به منابع معتبر خارجی میتواند اعتبار محتوای شما را نزد گوگل افزایش دهد.';
}
$checks[] = ['label' => $link_label, 'status' => $link_status, 'suggestion' => $suggestion];
// کلمه کلیدی در محتوا: ۱۰ امتیاز
$keyword_count = $this->count_keyword_as_whole_word( $content_text, $keyword );
if ( $keyword_count > 0 ) $score += 10;
$checks[] = ['label' => "کلمه کلیدی در محتوا (تعداد: $keyword_count)", 'status' => $keyword_count > 0, 'suggestion' => 'حداقل ۱ بار از کلمه کلیدی در پاراگراف اول استفاده کنید. چگالی مناسب بین ۰.۵% تا ۱% است.'];
// وجود اسکیما: ۱۰ امتیاز
if ( $schema_info['found'] ) $score += 10;
$checks[] = ['label' => 'وجود اسکیما (نوع: ' . esc_html($schema_info['type']) . ')', 'status' => $schema_info['found'], 'suggestion' => 'استفاده از نشانهگذاری اسکیمای ساختاریافته (مانند Article, FAQPage) به موتورهای جستجو کمک میکند محتوای شما را بهتر درک کنند.'];
// کلمه کلیدی در متن Alt: 10 امتیاز
$has_kw_in_alt = $this->check_keyword_in_image_alts( $post_id, $content_html, $keyword );
if ( $has_kw_in_alt ) $score += 10;
$checks[] = ['label' => 'کلمه کلیدی در متن Alt تصاویر', 'status' => $has_kw_in_alt, 'suggestion' => 'برای بهینهسازی تصاویر، کلمه کلیدی را در متن جایگزین (Alt) حداقل یکی از تصاویر محتوا یا تصویر شاخص به کار ببرید.'];
// مجموع کل امتیازات: ۱۰۰
return $this->render_progress_bar( 'امتیاز Aio', $score ) . $this->render_checklist( $checks );
}
private function analyze_geo( int $post_id, string $content_html, string $content_text, string $title, string $meta_desc, array $schema_info ): string {
$score = 0;
$checks = [];
$cities_pattern = 'تهران|اصفهان|شیراز|مشهد|تبریز|کرج|اهواز|قم|کرمانشاه|ارومیه|رشت|ساری|یزد|کرمان';
$full_text_for_city = $title . ' ' . $content_text . ' ' . $meta_desc;
$has_city_in_content = (bool) preg_match( "/\b({$cities_pattern})\b/u", $full_text_for_city );
if ($has_city_in_content) $score += 25;
$checks[] = ['label' => 'ذکر نام شهر در محتوا یا عنوان', 'status' => $has_city_in_content, 'suggestion' => 'نام شهر هدف (مثلاً تهران) را در عنوان، توضیحات متا یا متن اصلی ذکر کنید.'];
$local_seo_info = $this->get_local_seo_nap_info();
$has_contact = !empty($local_seo_info['phone']) || !empty($local_seo_info['address']) || (bool) preg_match( '/(\d{8,11}|\b\d{3,4}-\d{7,8}\b)|(خیابان|بلوار|میدان|کوچه|پلاک)/u', $content_text );
if ($has_contact) $score += 20;
$checks[] = ['label' => 'وجود شماره تماس یا بخشی از آدرس', 'status' => $has_contact, 'suggestion' => 'اطلاعات تماس (تلفن) و آدرس را در تنظیمات Local SEO افزونه سئو یا در محتوای صفحه وارد کنید.'];
$has_map = (bool) preg_match( '/<iframe[^>]+src=[\'\"][^\'\"]*google\.com\/(maps)/i', $content_html );
if ($has_map) $score += 20;
$checks[] = ['label' => 'جاسازی نقشه گوگل (Google Maps)', 'status' => $has_map, 'suggestion' => 'با قرار دادن نقشه گوگل در صفحه، موقعیت مکانی خود را به گوگل و کاربران به طور واضح نشان دهید.'];
if ( $schema_info['is_local'] ) $score += 25;
$checks[] = ['label' => 'استفاده از اسکیمای LocalBusiness', 'status' => $schema_info['is_local'], 'suggestion' => 'برای کسب و کارهای محلی، استفاده از اسکیمای LocalBusiness یا زیرمجموعههای آن برای نمایش در نتایج محلی گوگل حیاتی است.'];
$has_city_in_alt = false;
if (preg_match_all('/<img[^>]+alt=["\']([^"\']*)["\']/i', $content_html, $alt_matches)) {
foreach ($alt_matches[1] as $alt_text) {
if (preg_match("/\b({$cities_pattern})\b/u", $alt_text)) { $has_city_in_alt = true; break; }
}
}
if($has_city_in_alt) $score += 10;
$checks[] = ['label' => 'نام شهر در متن جایگزین (Alt) تصاویر', 'status' => $has_city_in_alt, 'suggestion' => 'نام شهر هدف را در متن جایگزین حداقل یکی از تصاویر صفحه به کار ببرید.'];
return $this->render_progress_bar( 'امتیاز GEO', $score ) . $this->render_checklist( $checks );
}
/* ========================================================
* توابع کمکی
* ======================================================== */
private function get_active_schema_info( int $post_id ): array {
$result = [ 'found' => false, 'type' => 'یافت نشد', 'is_local' => false ];
$found_types = [];
if ( class_exists( '\RankMath' ) ) {
$schemas = get_post_meta( $post_id, 'rank_math_schema', true );
if ( ! empty( $schemas ) && is_array( $schemas ) ) {
foreach ( array_keys( $schemas ) as $schema_type ) {
$found_types[] = ucfirst( $schema_type );
}
}
if ( empty( $found_types ) && class_exists('\RankMath\Helper') ) {
$post_type = get_post_type( $post_id );
$default_schema = \RankMath\Helper::get_settings( "titles.pt_{$post_type}_default_schema_type" );
if ( ! empty( $default_schema ) ) {
$found_types[] = ucfirst($default_schema);
} elseif ( in_array($post_type, ['post', 'page'])) {
$found_types[] = 'Article';
} elseif ( $post_type === 'product' ) {
$found_types[] = 'Product';
}
}
}
elseif ( function_exists( 'YoastSEO' ) ) {
$page_type = get_post_meta( $post_id, '_yoast_wpseo_schema_page_type', true );
$article_type = get_post_meta( $post_id, '_yoast_wpseo_schema_article_type', true );
if ( ! empty( $article_type ) && $page_type === 'WebPage' ) {
$found_types[] = $page_type;
$found_types[] = $article_type;
} elseif ( ! empty( $page_type ) ) {
$found_types[] = $page_type;
} elseif ( class_exists('WPSEO_Options') ){
$post_type = get_post_type($post_id);
$options_key = 'schema-page-type-' . $post_type;
$default_type = \WPSEO_Options::get( $options_key, 'WebPage' );
$found_types[] = $default_type;
}
}
if ( ! empty( $found_types ) ) {
$result['found'] = true;
$unique_types = array_unique( array_filter( $found_types ) );
$result['type'] = implode( ', ', $unique_types );
foreach ( $unique_types as $type ) {
if ( preg_match( '/(LocalBusiness|Restaurant|Store|ContactPage|AboutPage)/i', $type ) ) {
$result['is_local'] = true;
break;
}
}
}
return $result;
}
private function get_focus_keyword_from_meta( int $post_id ): string {
$keyword = get_post_meta( $post_id, 'rank_math_focus_keyword', true );
if ( ! empty( $keyword ) ) { $keywords = explode( ',', $keyword ); return trim( $keywords[0] ); }
$keyword = get_post_meta( $post_id, '_yoast_wpseo_focuskw', true );
if ( ! empty( $keyword ) ) { return trim( $keyword ); }
return '';
}
private function get_meta_description_from_meta( int $post_id ): string {
$desc = get_post_meta( $post_id, 'rank_math_description', true );
if ( ! empty( $desc ) ) return trim( $desc );
$desc = get_post_meta( $post_id, '_yoast_wpseo_metadesc', true );
if ( ! empty( $desc ) ) return trim( $desc );
return '';
}
private function keyword_exists_as_whole_word( string $haystack, string $needle ): bool {
if ( empty( trim( $haystack ) ) || empty( trim( $needle ) ) ) return false;
return preg_match( '/\b' . preg_quote( trim( $needle ), '/' ) . '\b/iu', $haystack ) === 1;
}
private function count_keyword_as_whole_word( string $haystack, string $needle ): int {
if ( empty( trim( $haystack ) ) || empty( trim( $needle ) ) ) return 0;
return preg_match_all( '/\b' . preg_quote( trim( $needle ), '/' ) . '\b/iu', $haystack );
}
private function is_post_indexable( int $post_id ): bool {
if ( class_exists( 'RankMath\Post' ) ) {
$robots = \RankMath\Post::get_meta( 'robots', $post_id );
if ( is_array( $robots ) && in_array( 'noindex', $robots, true ) ) return false;
}
if ( get_post_meta( $post_id, '_yoast_wpseo_meta-robots-noindex', true ) === '1' ) return false;
if ( '0' === get_option( 'blog_public' ) ) return false;
return true;
}
private function analyze_links_in_content( string $html ): array {
$counts = [ 'internal' => 0, 'external' => 0 ];
if ( empty( $html ) ) return $counts;
$site_host = wp_parse_url( home_url(), PHP_URL_HOST );
if ( ! $site_host ) return $counts;
$site_host = str_replace( 'www.', '', $site_host );
if ( ! class_exists('DOMDocument') ) return $counts;
libxml_use_internal_errors( true );
$dom = new DOMDocument();
@$dom->loadHTML( mb_convert_encoding( $html, 'HTML-ENTITIES', 'UTF-8' ) );
libxml_clear_errors();
foreach ( $dom->getElementsByTagName('a') as $link ) {
$href = $link->getAttribute('href');
if ( empty( $href ) || in_array( $href[0], [ '#', '?' ], true ) || strpos( $href, 'javascript:' ) === 0 || strpos($href, 'mailto:') === 0 ) continue;
$host = wp_parse_url( $href, PHP_URL_HOST );
if ( empty( $host ) || str_replace( 'www.', '', $host ) === $site_host ) {
$counts['internal']++;
} else {
$counts['external']++;
}
}
return $counts;
}
private function check_keyword_in_image_alts( int $post_id, string $content_html, string $keyword ): bool {
if ( preg_match_all( '/<img[^>]+alt=["\']([^"\']*)["\']/i', $content_html, $matches ) ) {
foreach ( $matches[1] as $alt_text ) {
if ( ! empty( $alt_text ) && $this->keyword_exists_as_whole_word( $alt_text, $keyword ) ) {
return true;
}
}
}
if ( function_exists( 'has_post_thumbnail' ) && has_post_thumbnail( $post_id ) ) {
$thumbnail_id = get_post_thumbnail_id( $post_id );
$featured_image_alt = get_post_meta( $thumbnail_id, '_wp_attachment_image_alt', true );
if ( ! empty( $featured_image_alt ) && $this->keyword_exists_as_whole_word( $featured_image_alt, $keyword ) ) {
return true;
}
}
return false;
}
private function get_local_seo_nap_info(): array {
$nap = [ 'address' => '', 'phone' => '' ];
if ( function_exists( 'rank_math_get_option' ) ) {
$nap['address'] = rank_math_get_option( 'local_seo_address' );
$nap['phone'] = rank_math_get_option( 'local_seo_phone' );
}
if ( empty( $nap['address'] ) && class_exists( 'WPSEO_Options' ) ) {
if ( is_plugin_active( 'wpseo-local/wpseo-local.php' ) ) {
$options = get_option( 'wpseo_local' );
$nap['address'] = $options['location_address'] ?? '';
$nap['phone'] = $options['location_phone'] ?? '';
}
}
return $nap;
}
private function get_provinces_list(): array {
return [
'آذربایجان شرقی' => 'تبریز', 'آذربایجان غربی' => 'ارومیه', 'اردبیل' => 'اردبیل', 'اصفهان' => 'اصفهان',
'البرز' => 'کرج', 'ایلام' => 'ایلام', 'بوشهر' => 'بوشهر', 'تهران' => 'تهران',
'چهارمحال و بختیاری' => 'شهرکرد', 'خراسان رضوی' => 'مشهد', 'خراسان جنوبی' => 'بیرجند', 'خراسان شمالی' => 'بجنورد',
'خوزستان' => 'اهواز', 'زنجان' => 'زنجان', 'سمنان' => 'سمنان', 'سیستان و بلوچستان' => 'زاهدان',
'فارس' => 'شیراز', 'قزوین' => 'قزوین', 'قم' => 'قم', 'کردستان' => 'سنندج',
'کرمان' => 'کرمان', 'کرمانشاه' => 'کرمانشاه', 'کهگیلویه و بویراحمد' => 'یاسوج', 'گلستان' => 'گرگان',
'گیلان' => 'رشت', 'لرستان' => 'خرمآباد', 'مازندران' => 'ساری', 'مرکزی' => 'اراک',
'هرمزگان' => 'بندرعباس', 'همدان' => 'همدان', 'یزد' => 'یزد'
];
}
private function render_progress_bar( string $label, int $score ): string {
$score = min( 100, max( 0, $score ) );
$color = '#dc3545';
if ( $score >= 80 ) $color = '#198754';
elseif ( $score >= 50 ) $color = '#ffc107';
$text_color = ( $score < 50 && $score > 0 ) ? '#000' : '#fff';
return sprintf('<div><strong>%s: %d/100</strong></div><div class="mini-seo-progress-bar"><div class="mini-seo-progress-bar-fill" style="width:%d%%; background-color:%s; color:%s;">%d%%</div></div>', esc_html($label), $score, $score, esc_attr($color), esc_attr($text_color), $score);
}
private function render_checklist( array $checks ): string {
$html = '<ul class="mini-seo-checklist">';
foreach ( $checks as $check ) {
$status_class = $check['status'] ? 'status-pass' : 'status-fail';
$label_html = esc_html( $check['label'] );
$html .= sprintf( '<li class="%s">%s</li>', esc_attr( $status_class ), $label_html );
// پیشنهاد فقط در صورتی نمایش داده میشود که وضعیت Fail باشد و متنی برای پیشنهاد وجود داشته باشد
if ( ! $check['status'] && ! empty( $check['suggestion'] ) ) {
$suggestion_text = preg_replace('/(https?:\/\/[^\s]+)/', '<a href="$1" target="_blank" rel="noopener noreferrer">$1</a>', esc_html( $check['suggestion'] ));
$html .= '<div class="mini-seo-suggestion">' . $suggestion_text . '</div>';
}
}
$html .= '</ul>';
return $html;
}
private function get_aio_tutorial_content(): string {
return '<h4>راهنمای Aio (بهینهسازی برای موتور پاسخگو)</h4><p>هدف <strong>Aio</strong>، ارائه پاسخی مستقیم، سریع و دقیق به سوالات کاربران است. با رعایت این موارد، شانس خود را برای نمایش در بخش پاسخهای ویژه گوگل (Featured Snippets) و کسب رتبههای بهتر افزایش میدهید.</p><p><strong>۱. کلمه کلیدی در عنوان و توضیحات متا:</strong> کلمه کلیدی باید در مهمترین بخشهای صفحه یعنی عنوان و توضیحات متا وجود داشته باشد.</p><p><strong>۲. وضعیت ایندکس:</strong> مهمترین فاکتور فنی! اگر صفحه روی حالت <code>NoIndex</code> باشد، گوگل آن را در نتایج نمایش نخواهد داد.</p><p><strong>۳. استفاده از زیرعنوان H2 با کلمه کلیدی:</strong> برای مثال، اگر کلمه کلیدی شما <code>وردپرس</code> است، یک تگ H2 مانند <code><h2>وردپرس چیست؟</h2></code> تیک سبز را دریافت میکند.</p><p><strong>۴. لینکسازی داخلی و خارجی:</strong> با لینک دادن به صفحات مرتبط در سایت خودتان (داخلی) و به منابع معتبر دیگر (خارجی)، اعتبار صفحه را افزایش میدهید.</p><p><strong>۵. وجود کلمه کلیدی در محتوا:</strong> کلمه کلیدی باید به تعداد طبیعی و مناسب در متن تکرار شود. از تکرار بیش از حد خودداری کنید.</p><p><strong>۶. اسکیما (Schema):</strong> اگر صفحه شما به سوالات متداول پاسخ میدهد یا یک مقاله است، با استفاده از اسکیمای مناسب (مثل `FAQPage` یا `Article`) به گوگل کمک میکنید تا محتوای شما را بهعنوان پاسخ مستقیم نمایش دهد.</p><p><strong>۷. متن جایگزین (Alt) تصاویر:</strong> نوشتن متن جایگزین مناسب با کلمه کلیدی، به درک بهتر گوگل از محتوای تصویری و بهبود سئوی کلی صفحه کمک میکند.</p>';
}
private function get_geo_tutorial_content(): string {
return '<h4>راهنمای GEO (بهینهسازی برای جستجوی محلی)</h4><p>هدف <strong>GEO</strong>، کسب رتبه برای جستجوهایی است که به یک موقعیت مکانی خاص مرتبط هستند (مثلاً <code>رستوران در تهران</code>). این تکنیکها برای کسبوکارهای محلی حیاتی است.</p><p><strong>۱. ذکر نام شهر:</strong> نام شهر هدف خود را در عنوان، تیترها، متن اصلی و همچنین <strong>متن جایگزین (Alt) تصاویر</strong> به کار ببرید.</p><p><strong>۲. اطلاعات تماس و آدرس (NAP):</strong> درج اطلاعات نام (Name)، آدرس (Address) و شماره تلفن (Phone) یک سیگنال بسیار قوی برای سئوی محلی است.</p><p><strong>۳. جاسازی نقشه گوگل:</strong> قرار دادن نقشه گوگل در صفحه، موقعیت مکانی شما را به گوگل رسماً اعلام میکند.</p><p><strong>۴. استفاده از اسکیمای LocalBusiness:</strong> این یک زبان نشانهگذاری است که به گوگل کمک میکند کسبوکار محلی شما را عمیقتر درک کند. اگر کسبوکار محلی دارید، استفاده از این اسکیما یا انواع خاصتر آن (مثل <code>Restaurant</code>, <code>Store</code>, <code>Dentist</code>) <strong>بسیار ضروری</strong> است.</p>';
}
}
Mini_SEO_Analyzor::instance();
endif;
افزونه مینی سئو نسخه پرو
تحلیلگر سئو AIO: بهینهسازی برای موتورهای پاسخگو
با تحلیلگر AIO، محتوای خود را برای پاسخ مستقیم به نیاز کاربر و نمایش در بخش پاسخهای ویژه گوگل (Featured Snippets) بهینه کنید.
-
✅ بررسی کلمه کلیدی: حضور هوشمندانه کلمه کلیدی در عنوان، متا، تیتر H2 و متن اصلی.
-
✅ وضعیت ایندکس: اطمینان از اینکه صفحه شما توسط گوگل دیده میشود.
-
✅ لینکسازی هوشمند: تحلیل کامل لینکهای داخلی و خارجی برای افزایش اعتبار.
-
✅ قدرت اسکیما: بررسی وجود اسکیماهای ساختاریافته برای درک بهتر محتوا توسط گوگل.
-
✅ سئوی تصاویر: تحلیل متن جایگزین (Alt) تصاویر برای گرفتن ورودی از گوگل ایمیج.
تحلیلگر سئو GEO: پادشاهی در جستجوهای محلی
اگر کسبوکار محلی دارید، این بخش بلیط طلایی شما برای جذب مشتریان اطرافتان است.
-
✅ شناسایی نام شهر: بررسی هوشمند نام شهر هدف در عنوان، محتوا و تصاویر.
-
✅ اطلاعات تماس (NAP): تحلیل وجود آدرس و شماره تلفن به عنوان یک سیگنال قدرتمند محلی.
-
✅ نقشه گوگل: تشخیص وجود نقشه گوگل برای تثبیت موقعیت مکانی شما.
-
✅ اسکیمای محلی: بررسی تخصصی اسکیمای LocalBusiness برای درخشش در نتایج محلی.
تحلیلگر Pico: بهینهسازی تصاویر و مدیا
سرعت سایت و تجربه کاربری را با تحلیل دقیق رسانهها به سطح بالاتری ببرید.
-
✅ فرمت WebP: شناسایی تصاویر قدیمی و پیشنهاد فرمت مدرن WebP برای افزایش سرعت.
-
✅ متن جایگزین (Alt): شناسایی تصاویری که متن جایگزین ندارند تا هیچ فرصتی را از دست ندهید.
-
✅ حجم و ابعاد استاندارد: تشخیص تصاویر سنگین و بسیار عریض که سرعت سایت را کم میکنند.
-
✅ شناسایی ویدیو و صوت: امتیازدهی مثبت برای استفاده از ویدیو و پادکست جهت افزایش زمان ماندگاری کاربر.
سازگاری و یکپارچگی کامل
-
هماهنگ با غولها: کاملاً سازگار با افزونههای محبوب Rank Math و Yoast SEO.
-
پشتیبانی از ووکامرس: تحلیل دقیق و تخصصی صفحات محصولات فروشگاه شما.
-
مناسب برای همه: طراحی شده برای نوشتهها، برگهها و محصولات ووکامرس.
- سفارش خرید نسخه پرو⬇️
2 پاسخ
نسخه پرو افزونه مینی سئو از نسخه پرو رنک مث هم بهتره! ۴۰تا فیلد مهم با راهنماش💖بهم داد
با رنک مث اوکیه!