تابع add_shortcode() در وردپرس ابزار رسمی ثبت یک شورتکد سفارشی است و به‌عنوان یکی از پرکاربردترین توابع افزونه‌نویسی، امکان تزریق محتوای پویا در نوشته‌ها و برگه‌ها بدون نوشتن HTML را فراهم می‌کند. بدون این تابع، کاربر برای هر تغییر کوچک در محتوا نیاز به ویرایش مستقیم قالب یا کد خواهد داشت.

تابع add_shortcode وردپرس یکی از پرکاربردترین توابع افزونه‌نویسی برای ساخت شورتکد سفارشی است. این تابع امکان ثبت tag، اتصال callback و کنترل خروجی را فراهم می‌کند و پایه ساخت محتوای پویا در نوشته‌ها محسوب می‌شود. در این راهنما ساختار کامل، پارامترها، نمونه‌های واقعی، اشتباهات رایج و نکات امنیتی این تابع بررسی می‌شود. همچنین تفاوت آن با do_shortcode و روش‌های بهینه پیاده‌سازی آن توضیح داده می‌شود. در پایان پرسش‌های پرتکرار و نگاه فنی عمیق به این تابع مرور خواهد شد.

در پروژه‌هایی که محتوای پویا داشتند، این تابع همیشه ابزار اصلی تیم محتوا بوده است. یک شورتکد بدون shortcode_atts به‌سرعت به یک منبع خطا تبدیل می‌شود و شورتکدی که به‌جای return از echo استفاده کند، خروجی را در جای اشتباه صفحه قرار می‌دهد.

چرا add_shortcode اهمیت دارد

وردپرس برای درج محتوای پویا در نوشته‌ها چند راه ارائه می‌دهد: استفاده از HTML مستقیم، استفاده از بلاک‌های گوتنبرگ و استفاده از شورتکد. شورتکد همچنان به‌عنوان ساده‌ترین و سریع‌ترین راه برای این کار باقی مانده است، به‌ویژه در پروژه‌هایی که کاربران غیرفنی محتوا را مدیریت می‌کنند.

تابع add_shortcode() ثبت یک tag را در آرایه سراسری $shortcode_tags انجام می‌دهد و اتصال callback را تضمین می‌کند. وقتی وردپرس محتوای یک نوشته را رندر می‌کند، به‌طور خودکار شورتکدهای ثبت‌شده را تشخیص می‌دهد و آن‌ها را با خروجی callback جایگزین می‌کند.

برای درک کامل چرخه اجرای شورتکد، مطلب تابع do_shortcode را مطالعه کنید.

ساختار و امضای تابع add_shortcode

امضای این تابع به شکل زیر است:

add_shortcode( string $tag, callable $callback ): void

پارامتر اول نام tag (بدون براکت) است. پارامتر دوم callback مسئول تولید خروجی. خروجی این تابع void است و فقط ثبت را انجام می‌دهد.

add_shortcode( 'my_button', 'my_button_shortcode_handler' );

function my_button_shortcode_handler( $atts, $content = null ) {
    // تولید خروجی
    return '<a class="btn">' . esc_html( $content ) . '</a>';
}

نکته مهم: callback باید خروجی را با return برگرداند، نه echo. این نکته در ادامه به‌تفصیل بررسی می‌شود.

پارامترها و قواعد انتخاب tag

پارامتر tag

نام شورتکد. این نام باید فقط شامل حروف کوچک لاتین، اعداد و زیرخط باشد. اگر از خط تیره یا کاراکترهای دیگر استفاده کنید، شورتکد ممکن است به‌درستی تشخیص داده نشود.

add_shortcode( 'my_button', ... );  // درست
add_shortcode( 'my-button', ... );  // ممکن است مشکل‌ساز باشد
add_shortcode( 'دکمه', ... );        // اشتباه، کاراکتر غیرلاتین

پیشوند اختصاصی برای جلوگیری از تداخل

اگر tag را بدون پیشوند ثبت کنید، احتمال تداخل با افزونه‌های دیگر بالا می‌رود. همیشه از پیشوند اختصاصی استفاده کنید:

add_shortcode( 'myplugin_button', ... );  // درست
add_shortcode( 'button', ... );            // ممکن است تداخل کند

پارامتر callback

callback می‌تواند یک تابع ساده، یک متد کلاس یا یک closure باشد. در پروژه‌های حرفه‌ای، معمولاً از متد کلاس استفاده می‌شود تا کد ساختاریافته‌تر باشد:

class MyPlugin_Shortcodes {
    public static function register() {
        add_shortcode( 'myplugin_button', array( __CLASS__, 'render_button' ) );
    }

    public static function render_button( $atts, $content = null ) {
        return '...';
    }
}
add_action( 'init', array( 'MyPlugin_Shortcodes', 'register' ) );

این الگو در افزونه‌های حرفه‌ای رایج است. برای مطالعه بیشتر، مطلب تابع do_action را ببینید.

ساخت callback حرفه‌ای با shortcode_atts

callback یک شورتکد معمولاً سه بخش دارد: تعریف مقادیر پیش‌فرض با shortcode_atts، تولید خروجی و return کردن آن.

function myplugin_button_handler( $atts, $content = null ) {
    $atts = shortcode_atts(
        array(
            'color' => 'blue',
            'size'  => 'medium',
            'url'   => '',
        ),
        $atts,
        'myplugin_button'
    );

    $classes = sprintf( 'btn btn-%s btn-%s', sanitize_html_class( $atts['color'] ), sanitize_html_class( $atts['size'] ) );

    return sprintf(
        '<a class="%s" href="%s">%s</a>',
        esc_attr( $classes ),
        esc_url( $atts['url'] ),
        esc_html( $content )
    );
}

نقش shortcode_atts

تابع shortcode_atts() مقادیر پیش‌فرض را با مقادیری که کاربر در شورتکد وارد کرده ترکیب می‌کند و فقط کلیدهای مجاز را نگه می‌دارد. بدون این تابع، هر پارامتر اضافی از کاربر به کد شما می‌رسد و می‌تواند به رفتار غیرمنتظره منجر شود.

نکته مهم: پارامتر سوم shortcode_atts که نام شورتکد است، اجازه می‌دهد افزونه‌های دیگر با فیلتر shortcode_atts_{tag} مقادیر پیش‌فرض را تغییر دهند.

نقش return در callback

callback شورتکد باید خروجی را با return برگرداند، نه echo. اگر از echo استفاده کنید، خروجی در ابتدای صفحه یا در جای اشتباه ظاهر می‌شود چون شورتکد در مرحله پردازش محتوا اجرا می‌شود و خروجی باید در جای اصلی خود باقی بماند:

// اشتباه، خروجی در جای اشتباه قرار می‌گیرد
function wrong_handler() {
    echo '<p>متن</p>';
}

// درست
function correct_handler() {
    return '<p>متن</p>';
}

نمونه‌های عملی در پروژه واقعی

شورتکد دکمه با پارامترهای قابل تنظیم

add_action( 'init', function () {
    add_shortcode( 'myplugin_cta', 'myplugin_cta_handler' );
} );

function myplugin_cta_handler( $atts, $content = null ) {
    $atts = shortcode_atts(
        array(
            'url'    => '#',
            'style'  => 'primary',
            'target' => '_self',
        ),
        $atts,
        'myplugin_cta'
    );

    $target = in_array( $atts['target'], array( '_self', '_blank' ), true ) ? $atts['target'] : '_self';
    $rel = '_blank' === $target ? ' rel="noopener"' : '';

    return sprintf(
        '<a href="%s" class="cta cta-%s" target="%s"%s>%s</a>',
        esc_url( $atts['url'] ),
        esc_attr( $atts['style'] ),
        esc_attr( $target ),
        $rel,
        esc_html( $content )
    );
}

نکته مهم در این الگو: مقدار target با in_array محدود شده تا از تزریق مقادیر ناخواسته در attribute جلوگیری شود.

شورتکد نمایش آخرین پست‌ها

function myplugin_recent_posts_handler( $atts ) {
    $atts = shortcode_atts(
        array(
            'count' => 5,
            'cat'   => '',
        ),
        $atts,
        'myplugin_recent_posts'
    );

    $args = array(
        'posts_per_page' => absint( $atts['count'] ),
        'post_status'    => 'publish',
    );

    if ( ! empty( $atts['cat'] ) ) {
        $args['category_name'] = sanitize_title( $atts['cat'] );
    }

    $posts = get_posts( $args );
    if ( empty( $posts ) ) {
        return '';
    }

    $output = '<ul class="recent-posts">';
    foreach ( $posts as $post ) {
        $output .= sprintf(
            '<li><a href="%s">%s</a></li>',
            esc_url( get_permalink( $post ) ),
            esc_html( get_the_title( $post ) )
        );
    }
    $output .= '</ul>';

    return $output;
}
add_shortcode( 'myplugin_recent_posts', 'myplugin_recent_posts_handler' );

در این الگو از تابع get_posts برای دریافت پست‌ها استفاده شده است.

شورتکد محصور با محتوای درونی

function myplugin_box_handler( $atts, $content = null ) {
    $atts = shortcode_atts(
        array( 'style' => 'info' ),
        $atts,
        'myplugin_box'
    );

    $content = do_shortcode( $content );

    return sprintf(
        '<div class="box box-%s">%s</div>',
        esc_attr( $atts['style'] ),
        wp_kses_post( $content )
    );
}

نکته مهم: فراخوانی do_shortcode روی content اجازه می‌دهد شورتکدهای درونی نیز پردازش شوند. برای مطالعه بیشتر، مطلب تابع do_shortcode را ببینید.

شورتکد با فرم ورودی کاربر

function myplugin_search_handler( $atts ) {
    $atts = shortcode_atts( array( 'placeholder' => 'جستجو...' ), $atts );

    return sprintf(
        '<form role="search" method="get" action="%s"><input type="search" name="s" placeholder="%s" /></form>',
        esc_url( home_url( '/' ) ),
        esc_attr( $atts['placeholder'] )
    );
}

شورتکد فقط برای کاربران وارد‌شده

function myplugin_member_content_handler( $atts, $content = null ) {
    if ( ! is_user_logged_in() ) {
        return '<p>' . esc_html__( 'برای مشاهده این محتوا وارد شوید.', 'my-plugin' ) . '</p>';
    }
    return '<div class="member-content">' . wp_kses_post( do_shortcode( $content ) ) . '</div>';
}

برای مطالعه کامل نقش‌ها، مطلب Capability و نقش‌های کاربری سفارشی را ببینید.

ساخت کلاس اختصاصی برای شورتکدها

class MyPlugin_Shortcodes {

    public function __construct() {
        add_action( 'init', array( $this, 'register' ) );
    }

    public function register() {
        add_shortcode( 'myplugin_button', array( $this, 'button' ) );
        add_shortcode( 'myplugin_box', array( $this, 'box' ) );
    }

    public function button( $atts, $content = null ) { /* ... */ }
    public function box( $atts, $content = null ) { /* ... */ }
}

این الگو در افزونه‌های با معماری کلاس‌محور رایج است. برای مطالعه بیشتر، مطلب استانداردهای PSR در PHP را ببینید.

حذف شورتکد

برای حذف یک شورتکد از سایت، می‌توانید از remove_shortcode استفاده کنید:

remove_shortcode( 'myplugin_button' );

یا با hook remove_filter طبق الگوهای مشابه. مطلب تابع remove_filter راهنماست.

اشتباهات رایج در استفاده از add_shortcode

echo به جای return در callback

شایع‌ترین اشتباه. اگر در callback از echo استفاده کنید، خروجی در جای اشتباه صفحه ظاهر می‌شود. همیشه باید return کنید.

نبود shortcode_atts

اگر پارامترها را با shortcode_atts نرمال‌سازی نکنید، هر مقدار اضافی از کاربر به کد شما می‌رسد. این می‌تواند به خطا یا حتی نقص امنیتی منجر شود:

// اشتباه
$color = $atts['color'];

// درست
$atts = shortcode_atts( array( 'color' => 'blue' ), $atts );
$color = $atts['color'];

نبود escape در خروجی

اگر مقادیر پارامترها را در HTML چاپ کنید بدون escape، به XSS منجر می‌شود. همیشه از esc_html، esc_attr و esc_url استفاده کنید. مطلب Output Escaping در وردپرس راهنمای کامل است.

استفاده از tag بدون پیشوند

اگر tag را بدون پیشوند ثبت کنید، احتمال تداخل با افزونه‌های دیگر بالا می‌رود. همیشه از پیشوند اختصاصی مثل myplugin_ استفاده کنید.

نبود بررسی محتوای درونی

اگر شورتکد محتوای درونی دارد، باید آن را با do_shortcode پردازش کنید تا شورتکدهای درونی نیز اجرا شوند. بدون این کار، شورتکدهای تو در تو کار نمی‌کنند.

نبود بررسی user input در attribute

اگر در attribute مقدار URL می‌گیرید، همیشه با esc_url خروجی بگیرید. اگر مقدار از کاربر می‌آید، در سمت ورودی نیز باید sanitize شود. مطلب راهنمای Sanitization مرجع است.

نبود تست روی سناریوهای مرزی

تست‌هایی مثل «شورتکد بدون پارامتر»، «شورتکد با پارامتر اضافی»، «شورتکد در برگه» و «شورتکد داخل شورتکد» را حتماً بنویسید.

امنیت و عملکرد در add_shortcode

شورتکد یک نقطه ورود داده از محتوا به کد است. لایه‌های ضروری امنیتی:

  • sanitize ورودی‌ها با shortcode_atts و توابع sanitize
  • escape خروجی‌ها با esc_html، esc_attr، esc_url
  • محدود کردن attributeهای مجاز با whitelist
  • در شورتکدهای محصور، محتوای درونی را با wp_kses_post پاک کنید
  • در شورتکدهایی که با دیتابیس کار می‌کنند، از prepared statement استفاده کنید

برای مطالعه جامع مباحث امنیتی، مطلب SQL Injection Prevention در وردپرس مرجع است.

از نظر عملکرد، هر شورتکد در زمان پردازش محتوا اجرا می‌شود. اگر callback کوئری سنگین داشته باشد، در هر بار نمایش نوشته یک کوئری اضافه اجرا می‌شود. توصیه می‌شود:

  1. نتیجه کوئری‌های پرتکرار را در cache ذخیره کنید
  2. از Transient برای داده‌های تازه استفاده کنید
  3. در صورت امکان، از بلاک گوتنبرگ با Server Side Rendering استفاده کنید

برای مطالعه الگوهای بهینه، مطلب تابع get_transient و تابع set_transient راهنماست.

پرسش‌های پرتکرار درباره add_shortcode

تفاوت add_shortcode با do_shortcode چیست؟

add_shortcode() یک شورتکد جدید ثبت می‌کند، در حالی که do_shortcode() یک متن حاوی شورتکد را پردازش و شورتکدهای درونی را اجرا می‌کند.

چرا شورتکد من در محتوا ظاهر می‌شود اما اجرا نمی‌شود؟

معمولاً به دلیل نبود add_shortcode در hook init، یا نبود do_shortcode در فیلدهای سفارشی. وردپرس به‌طور پیش‌فرض فقط محتوای نوشته را پردازش می‌کند.

آیا می‌توان شورتکد را در ویجت استفاده کرد؟

در ویجت بلاک، بله. در ویجت کلاسیک، باید از فیلتر widget_text با do_shortcode استفاده کنید. مطلب تابع register_widget راهنماست.

آیا می‌توان شورتکد را در قالب PHP فراخوانی کرد؟

بله، با echo do_shortcode( '[myplugin_button]' );. اما توصیه می‌شود در قالب از توابع PHP مستقیم استفاده کنید نه شورتکد.

آیا می‌توان چند شورتکد در یک نوشته داشت؟

بله، بی‌نهایت. هر شورتکد به‌صورت مستقل پردازش می‌شود.

آیا می‌توان شورتکد را در فیلد سفارشی (Custom Field) استفاده کرد؟

خیر، وردپرس فیلدهای سفارشی را پردازش نمی‌کند. باید در زمان نمایش مقدار فیلد، خودتان do_shortcode را فراخوانی کنید:

$value = get_post_meta( get_the_ID(), 'my_field', true );
echo do_shortcode( $value );

مطلب تابع get_post_meta راهنماست.

آیا شورتکد در محتوای بلاک گوتنبرگ کار می‌کند؟

بله، در بلاک Shortcode و بلاک Paragraph کار می‌کند. اما در بلاک‌های دیگر که خروجی را از HTML مستقیم می‌سازند، ممکن است کار نکند.

نگاه فنی عمیق به add_shortcode

در سطح معماری، add_shortcode() یک رکورد در آرایه سراسری $shortcode_tags ثبت می‌کند. این آرایه در فایل wp-includes/shortcodes.php تعریف شده و توسط تابع do_shortcode پیمایش می‌شود. کل چرخه پردازش شورتکد با یک regex پیچیده انجام می‌شود که در get_shortcode_regex تعریف شده است.

نکته ظریف اول، مسئله ترتیب اجرا در hook است. اگر add_shortcode را در hook init ثبت کنید اما محتوا قبل از آن پردازش شود (مثلاً در یک rest API)، شورتکد اجرا نمی‌شود. برای مطالعه هوک‌ها، مطلب تابع do_action را ببینید.

نکته دوم، مسئله ترتیب nested shortcode است. اگر یک شورتکد داخل شورتکد دیگری باشد، به‌طور پیش‌فرض پردازش تو در تو انجام نمی‌شود. باید در callback شورتکد بیرونی، do_shortcode را روی content فراخوانی کنید.

مسئله سوم، تعامل با Widget Block و سایر اجزای مدرن است. بلاک Shortcode از همان مکانیزم استفاده می‌کند اما محدودیت‌هایی دارد. اگر شورتکد شما از پارامترهای پیچیده استفاده می‌کند، در بلاک Shortcode باید به‌صورت دقیق نوشته شود.

در نهایت، در پروژه‌های Enterprise توصیه می‌شود به‌جای شورتکدهای مستقل، یک کلاس Registry مرکزی بسازید که تمام شورتکدها را به‌صورت declarative ثبت کند. این کار از پخش شدن منطق جلوگیری می‌کند و تست‌پذیری را بالا می‌برد. برای مطالعه بیشتر، مباحث WordPress Components و استانداردهای PSR مفید هستند. برای مطالعه بیشتر درباره خود وردپرس، WordPress در ویکی‌پدیا نقطه شروع خوبی است.

اگر در پروژه‌ای با مشکل نمایش شورتکد در فیلد سفارشی یا ناسازگاری با بلاک گوتنبرگ مواجه شده‌اید، برای ما جالب است بدانید کدام راهکار عملاً به حل مسئله کمک کرده است. تجربه خود را در دیدگاه‌ها بنویسید تا برای سایر توسعه‌دهندگان هم مفید باشد.