Post Templet

الملف يستخدم دوال WordPress الأساسية داخل حلقة The Loop ويحتوي بعض نقاط الضعف (عدم فحص الأخطاء، عدم الهروب/التعقيم، روابط فارغة، طباعة مباشرة بدون تحقّق). سأشرح كل دالة أولاً ثم أعرض قالبًا مُحسَّنًا.

شرح الدوال والعبارات الموجودة في الكود

سأعرض: توقيع الدالة (Parameters)، ما ترجع، شرح سلوكي، بدائل/نصائح، وأفضل ممارسات/ملاحظات.

global $post;
  • نوع: عبارة PHP (ليس دالة)
  • وظيفته: يتيح الوصول للكائن $post العام (وهو WP_Post) داخل نطاق دالة/قالب.
  • ملاحظات/أفضل ممارسات:
    • داخل حلقة WP القياسية (while ( have_posts() ) { the_post(); ... }) لا تحتاج لذلك عادة لأن the_post() يعدّل $post.
    • عند استخدام WP_Query مخصص استخدم setup_postdata( $post ) و wp_reset_postdata() أو global $post إذا تحتاج الوصول المباشر.
    • لا تزيل wp_reset_postdata() بعد استعلام مخصص لتفادي كسر الحلقة الافتراضية.
get_header( $name = null )
  • Parameters: $name (string|null) — لو وُضع get_header('home') سيحاول تحميل header-home.php وإلا header.php.
  • ترجع: لا ترجع قيمة؛ تقوم بضم ملف القالب.
  • بدائل/ملاحظات:
    • بديل مرن: get_template_part( 'template-parts/header', 'home' ) إذا أردت إدارة أجزاء قابلة لإعادة الاستخدام.
    • أفضل ممارسة: استخدم get_header() في أعلى ملفات القوالب. لا تخرج HTML قبل استدعاءها إن كان هناك اعتماد على الـheaders.

have_posts()

  • Parameters: لا شيء (يستخدم الـWP Loop أو استعلام مخصص).
  • ترجع: boolean — هل يوجد منشور متبقي في حلقة الاستعلام الحالي؟
  • ملاحظات:
    • تُستخدم عادة مع while ( have_posts() ) : the_post(); endwhile;
    • عند استخدام WP_Query مخصص: while ( $query->have_posts() ) { $query->the_post(); ... }
the_post()
  • Parameters: لا شيء.
  • وظيفة: يعدّل الـglobal $post, يستدعي setup_postdata( $post )، وتهيئ بيانات القالب (post data).
  • ملاحظات: لا تستخدم the_post() خارج حلقة من دون قصد. بعد حلقة مخصصة، نفّذ wp_reset_postdata().
get_the_title( $post = 0 )
  • Parameters: $post (ID|WP_Post|0) — افتراضيًا الحالية في الـLoop.
  • ترجع: string — عنوان المنشور (غير مؤمن).
  • مقارنة: the_title() تطبع العنوان مباشرة، get_the_title() ترجع النص.
  • أفضل ممارسات: عند الطباعة استخدم esc_html( get_the_title() ) أو تنسيق مناسب للـHTML، أو استخدم the_title( '<h1>', '</h1>', true ) إذا أردت إخراج آمن مع فلتر the_title.
get_the_terms( $post_id, $taxonomy )
  • Parameters:
    • $post_id (int|WP_Post)
    • $taxonomy (string) مثل 'category', 'post_tag', أو تاكسونومي مُعرّفة.
  • ترجع: مصفوفة WP_Term على النجاح، false إن لم توجد، أو WP_Error في حالة خطأ.
  • ملاحظات/أفضل ممارسات:
    • يجب فحص النتيجة: if ( $terms && ! is_wp_error( $terms ) ) { ... }
    • بدائل:
      • get_the_category() خاص بالـcategories (يرجع مصفوفة) — أسهل للاستخدام مع التصنيف الافتراضي.
      • wp_get_post_terms( $post_id, $taxonomy, $args ) إن أردت تحكماً أكبر (ترتيب، حقول مرجعية…).
    • الهروب: عند طباعة اسم التصنيف استخدم esc_html( $term->name ) وعند طباعة الرابط استخدم esc_url( get_term_link( $term ) ).
get_term_link( $term, $taxonomy = '' )
  • Parameters:
    • $term (term ID|WP_Term|string slug)
    • $taxonomy اختياري (عند تمرير slug)
  • ترجع: URL (string) أو WP_Error.
  • ملاحظات: افحص is_wp_error() قبل الطباعة. استخدم esc_url() عند إخراج الرابط.
get_the_ID()
  • Parameters: لا شيء (في الـLoop يرجع ID الحالية).
  • ترجع: int — معرف المنشور الحالي.
  • ملاحظة: بديل: get_post()->ID أو $post->ID إذا لديك $post.
get_year_link( $year )
  • Parameters: $year (int|string) مثل 2025.
  • ترجع: رابط أرشيف السنة (URL).
  • ملاحظة: عند إخراج الرابط استخدم esc_url().
get_the_date( $format = '', $post = null )
  • Parameters:
    • $format — تنسيق التاريخ مثل 'j M, Y' أو اتركه فارغًا لاستخدام إعدادات WP.
    • $post — ID أو WP_Post (افتراضي: المنشور الحالي).
  • ترجع: string (التاريخ منسق).
  • نصائح: استخدم تنسيق منطقي للغة. لتدويل النص استخدم date_i18n() إن لزم.
get_permalink( $post = 0 )
  • Parameters: $post (ID|WP_Post|null)
  • ترجع: string — رابط ثابت للمنشور/صفحة.
  • ملاحظات:
    • عند بناء روابط لمشاركة على الشبكات الاجتماعية، تأكد من ترميز/تعقيم الرابط: esc_url( add_query_arg( 'u', rawurlencode( get_permalink() ), 'https://www.facebook.com/sharer/sharer.php' ) ).
get_the_author_meta( $field = '', $user_id = false )
  • Parameters:
    • $field — مثل 'display_name', 'description', 'ID', 'user_email'، إلخ.
    • $user_id — اختياري، افتراضيًا مؤلف الـpost الحالي.
  • ترجع: string أو قيمة الحقل.
  • نصائح أمان: لا تخرج الحقول الحساسة (مثل البريد) علناً بدون مبرر. للروابط إلى صفحة الكاتب استخدم get_author_posts_url( $user_id ).
the_post_thumbnail( $size = 'post-thumbnail', $attr = '' )
  • Parameters:
    • $size — مثل 'thumbnail', 'medium', 'large', أو مصفوفة أبعاد أو اسم حجم مخصص.
    • $attr — مصفوفة أو string من سمات <img> (alt, class, loading …).
  • وظيفة: تطبع HTML لصورة المرفق المميزة (featured image).
  • بدائل: get_the_post_thumbnail( $post_id, $size, $attr ) — يعيد HTML بدلاً من الطباعة.
  • نصائح: تحقق أولاً has_post_thumbnail() لتفادي طباعة فراغ. أضف loading="lazy" و alt مناسب، أو استخدم wp_get_attachment_image_srcset() للتحكم بالـsrcset.
the_content( $more_link_text = null, $strip_teaser = false )
  • Parameters:
    • $more_link_text — نص رابط “اقرأ المزيد”.
    • $strip_teaser — boolean.
  • وظيفة: يطبع محتوى المنشور بعد تطبيق فلترات the_content (shortcodes, oEmbeds, autop, wpautop…).
  • ملاحظات: the_content() يعالج أمانياً إلى حد ما (يعتمد على فلتر the_content و wp_kses_post() عند الحاجة). إذا أردت جلب المحتوى كسلسلة استخدم get_the_content() ثم apply_filters('the_content', $content).
get_next_post() و get_previous_post()
  • Parameters: لا شيء (تعمل على المنشور الحالي).
  • ترجع: كائن WP_Post أو null.
  • بدائل: get_adjacent_post( $in_same_term, $excluded_terms, $previous ) (أكثر مرونة) أو استخدام previous_post_link() و next_post_link() (تطبع مباشرة روابط جاهزة).
  • نصائح:
    • افحص القيمة قبل الاستخدام: if ( $prev = get_previous_post() ) { ... } لأنها قد تُعيد null.
    • استخدم get_permalink( $prev ) و get_the_title( $prev ) أو esc_html( $prev->post_title ).
get_footer( $name = null )
  • مثل get_header() لكن لضم footer.php أو footer-$name.php.
ملاحظات أمنية وأفضل ممارسات عامة
  1. التحقّق من الأخطاء: دوال مثل get_the_terms() و get_term_link() قد تُعيد WP_Error. افحص دائمًا is_wp_error().
  2. التعقيم (Escaping):
    • الروابط: esc_url( $url )
    • نصوص داخل HTML: esc_html( $text )
    • سمات: esc_attr( $value )
    • إذا كنت تسمح بــHTML محدد: wp_kses_post() أو wp_kses( $html, $allowed )
  3. الفرق بين get_ و the_: get_ ترجع قيمة، the_ تطبع — استخدم get_ إذا تريد تعقيم/تعديل قبل الطباعة.
  4. الترجمة (i18n): إستخدم __(), _e(), esc_html__() عند طباعة نصوص ثابتة (مثلاً 'Tags:') لتمكين الترجمة.
  5. التحقّقات قبل الطباعة: تحقق أن متغيّر الـpost أو الكائنات موجودة قبل الوصول لخصائصها (if ( $prev_post ) ...).
  6. استعمال HTML سليم و semantic tags: عناوين <h1>..</h1>, <nav> للروابط السابقة/التالية، وصفات وصول (aria-*).
  7. الصور: استخدم أحجام مناسبة للصور (the_post_thumbnail('large'))، استخدم loading="lazy", تحقق من وجود الصورة has_post_thumbnail().
أخطاء واضحة في الشيفرة الحالية (ومقترحات إصلاح)
  1. عدم فحص $categories قبل الforeach → قد يؤدي إلى تحذير إذا كانت false أو WP_Error.
    إصلاح: if ( $categories && ! is_wp_error( $categories ) ) { foreach (...) { ... } }
  2. طباعة $tag->name داخل سلسلة محاطة بعلامات اقتباس مفردة (في النسخة المعلّقة) — سيطبع $tag->name كسلسلة حرفية. يجب فك الــconcatenation أو استخدام double quotes مع المتغير.
  3. روابط الكاتب فارغة (<a href="">) — لو أردت الربط بصفحة الكاتب استخدم get_author_posts_url( get_the_author_meta('ID') ).
  4. عدم فحص get_next_post() و get_previous_post() — يمكن أن تكون null. الكود الحالي يفترض وجودهم.
  5. عدم تعقيم المخرجات — عناوين، روابط، أسماء التصنيفات، إلخ تُطبع مباشرة. يجب استخدام esc_html, esc_url.
  6. تنسيق التاريخ get_the_date("d M,Y",get_the_ID()) — مسافة بعد الفاصلة مفقودة وget_the_ID() غير ضرورية داخل الـLoop.
نسخة مُحسنة (مُقترح للقالب) — مع التعقيم والفحوصات والـi18n
<?php
global $post;
get_header();

if ( have_posts() ) {
    while ( have_posts() ) {
        the_post();
        ?>
        <div class="post-bunner">
            <div class="container">
                <h2 class="post-title"><?php echo esc_html( get_the_title() ); ?></h2>
            </div>
        </div>

        <?php
        // Categories
        $categories = get_the_terms( get_the_ID(), 'category' );
        if ( $categories && ! is_wp_error( $categories ) ) {
            echo '<div class="post-categories">';
            foreach ( $categories as $category ) {
                $term_link = get_term_link( $category );
                if ( ! is_wp_error( $term_link ) ) {
                    printf(
                        '<span class="badge text-bg-secondary"><a href="%s">%s</a></span> ',
                        esc_url( $term_link ),
                        esc_html( $category->name )
                    );
                }
            }
            echo '</div>';
        }

        // Date (link to year archive)
        $year_link = get_year_link( (int) get_the_date( 'Y' ) );
        printf(
            '<p><a href="%s">%s</a></p>',
            esc_url( $year_link ),
            esc_html( get_the_date( 'j M, Y' ) )
        );

        // Share URL (Facebook)
        $share_url = esc_url( add_query_arg( 'u', get_permalink(), 'https://www.facebook.com/sharer/sharer.php' ) );
        ?>
        <p><a href="<?php echo $share_url; ?>"><?php esc_html_e( 'Share on Facebook', 'your-textdomain' ); ?></a></p>

        <!-- Tags -->
        <div class="tags">
            <p><?php esc_html_e( 'Tags:', 'your-textdomain' ); ?></p>
            <?php
            $tags = get_the_terms( get_the_ID(), 'post_tag' );
            if ( $tags && ! is_wp_error( $tags ) ) {
                foreach ( $tags as $tag ) {
                    $tag_link = get_term_link( $tag );
                    if ( ! is_wp_error( $tag_link ) ) {
                        printf(
                            '<span class="badge text-bg-secondary m-1"><a href="%s">%s</a></span>',
                            esc_url( $tag_link ),
                            esc_html( $tag->name )
                        );
                    }
                }
            } else {
                echo '<span class="no-tags">' . esc_html__( 'No tags', 'your-textdomain' ) . '</span>';
            }
            ?>
        </div>

        <!-- Author -->
        <?php
        $author_id = get_the_author_meta( 'ID' );
        printf(
            '<p>By: <a href="%s">%s</a></p>',
            esc_url( get_author_posts_url( $author_id ) ),
            esc_html( get_the_author_meta( 'display_name', $author_id ) )
        );

        // Author bio (optional)
        $author_desc = get_the_author_meta( 'description', $author_id );
        if ( $author_desc ) {
            printf( '<p class="author-desc">%s</p>', esc_html( $author_desc ) );
        }
        ?>

        <div class="container">
            <?php
            if ( has_post_thumbnail() ) {
                // use get_the_post_thumbnail if you want to capture or modify the markup
                the_post_thumbnail( 'large', array( 'loading' => 'lazy', 'class' => 'img-fluid', 'alt' => esc_attr( get_the_title() ) ) );
            }
            ?>
        </div>

        <div class="container content">
            <div class="post-content">
                <?php the_content(); ?>

                <hr>
                <nav class="post-navigation" aria-label="<?php esc_attr_e( 'Post Navigation', 'your-textdomain' ); ?>">
                    <?php
                    $prev_post = get_previous_post();
                    if ( $prev_post ) {
                        printf(
                            '<div class="nav-previous"><a href="%s" rel="prev">%s</a></div>',
                            esc_url( get_permalink( $prev_post ) ),
                            esc_html( get_the_title( $prev_post ) )
                        );
                    }

                    $next_post = get_next_post();
                    if ( $next_post ) {
                        printf(
                            '<div class="nav-next"><a href="%s" rel="next">%s</a></div>',
                            esc_url( get_permalink( $next_post ) ),
                            esc_html( get_the_title( $next_post ) )
                        );
                    }
                    ?>
                </nav>
            </div>

            <aside class="post-sidebar">
                <!-- Sidebar: ضع هنا ودجت أو محتوى ديناميكي -->
                <?php if ( is_active_sidebar( 'single-post-sidebar' ) ) : ?>
                    <?php dynamic_sidebar( 'single-post-sidebar' ); ?>
                <?php else : ?>
                    <p><?php esc_html_e( 'Sidebar content here', 'your-textdomain' ); ?></p>
                <?php endif; ?>
            </aside>
        </div>

        <?php
    } // end while
} // end if have_posts()

get_footer();
PHP

تحسينات إضافية وتلميحات عملية

  • استخدم wp_nonce عند تنفيذ أفعال قابلة للتغيير (ليس هنا لأن القالب للعرض فقط).
  • تقليل عدد الاستدعاءات داخل الحلقة: لا تعيد استدعاء get_the_ID() مرات كثيرة — خزّنها في متغيّر محلي إذا استُخدمت بكثرة.
  • استخدم template parts (مثل get_template_part( 'template-parts/content', 'single' );) لتقسيم القالب وجعله قابل لإعادة الاستخدام والاختبار.
  • تحسين الأداء: استخدم أحجام صور مُناسبة، CDN للصور، وخادم تخزين مؤقت (caching).
  • SEO & Accessibility: ضع rel="prev"/rel="next" للروابط، استخدم عناوين منطقية، أضف وصف بديل (alt) للصور مفيد وسليم.
  • التحقق من الترميزات: عند بناء روابط الشبكات الاجتماعية، كن واعيًا للـURL encoding — استخدم rawurlencode() أو add_query_arg().
خاتمة قصيرة

قدمت شرحًا مفصلاً لكل دالة وردت في ملفك، مع برامترات، نواتج، بدائل، ونصائح أمان وأداء، ثم قدّمت قالبًا محسّنًا قابل للنسخ واللصق. إن أردت، أستطيع الآن:

  • تحويل القالب إلى جزءين (template-parts) أو
  • إضافة دعم breadcrumb أو structured data (JSON-LD) للـSEO، أو
  • مراجعة قالبك بالكامل وتطبيق تحسينات CSS/JS (مثلاً Bootstrap-friendly) — أخبرني أي خيار تريده وسأنفذه مباشرة.
📝 معلومات البوست (Post Info)
  • the_ID()
    → تطبع معرف البوست مباشرة (بديل لـ get_the_ID() التي ترجع القيمة).
  • post_class( $class = '', $post_id = null )
    → تطبع CSS classes ديناميكية للبوست (مثل post, type-post, category-news).
    → مفيدة جدًا لتخصيص الاستايل حسب نوع البوست أو التصنيف.
  • the_excerpt() / get_the_excerpt()
    → ملخص البوست (excerpt) بدلًا من المحتوى الكامل.
    → مفيد في أماكن مثل المقالات ذات الصلة أو previews.
  • edit_post_link( $text, $before, $after )
    → تطبع رابط “تعديل” للبوست يظهر فقط للمستخدمين المسموح لهم.
  • get_post_meta( $post_id, $key, $single )
    → تجلب الحقل المخصص (custom field). ممتاز إذا عندك بيانات إضافية للبوست.
👤 معلومات الكاتب
  • the_author() / get_the_author()
    → بديل أسرع لـ get_the_author_meta('display_name').
  • get_author_posts_url( $author_id )
    → رابط صفحة الكاتب (تستخدم مع the_author() لعمل رابط صحيح).
  • the_author_posts_link()
    → تطبع اسم الكاتب كرابط إلى صفحة أرشيف مقالاته مباشرة.
🏷️ التصنيفات والتاجات
  • the_category( $separator )
    → تطبع روابط التصنيفات التي ينتمي لها البوست.
    → أبسط من get_the_terms().
  • the_tags( $before, $sep, $after )
    → تطبع التاجات كرابط أو نص.
🖼️ الصور والمرفقات
  • has_post_thumbnail()
    → فحص قبل طباعة الصورة البارزة.
  • wp_get_attachment_image( $attachment_id, $size, $icon, $attr )
    → تعرض صورة من مكتبة الوسائط (غير الصورة البارزة).
  • get_children( $args )
    → للحصول على مرفقات البوست (صور/ملفات). مفيد لعرض جاليري.
🔗 روابط الملاحة
  • previous_post_link( $format, $link, $in_same_term, $excluded_terms, $taxonomy )
  • next_post_link( $format, $link, $in_same_term, $excluded_terms, $taxonomy )
    → تطبع روابط التنقل للبوست السابق/التالي (أسهل من get_previous_post() + بناء الرابط يدويًا).
  • posts_nav_link() و paginate_links()
    → للتنقل بين الصفحات (مفيد أكثر في الأرشيفات).
💬 التعليقات
  • comments_template( $file, $separate_comments )
    → استدعاء قالب التعليقات (عادةً comments.php).
  • get_comments_number()
    → عدد التعليقات.
  • comments_popup_link()
    → تطبع رابط للتعليقات (مثل “3 Comments” أو “No Comments”).
📌 أخرى مفيدة
  • get_post_type() / get_post_type_object()
    → معرفة نوع البوست (post, page, custom post).
  • the_time( $format ) / get_the_time()
    → وقت نشر البوست (ساعة/دقيقة).
  • the_modified_date() / the_modified_time()
    → تاريخ/وقت آخر تعديل للبوست.
  • get_adjacent_post()
    → دالة أعمّ من get_previous_post() / get_next_post() وتسمح بتحديد taxonomy معين.
  • get_search_form()
    → تضمين نموذج البحث داخل القالب.
  • dynamic_sidebar()
    → لعرض Widgets في مكان جانبي مخصص (بدل كتابة نص ثابت في الـsidebar).
✨ أفضل الممارسات
  • عند عرض نصوص: استخدم esc_html().
  • عند عرض روابط: استخدم esc_url().
  • استخدم Template Tags الجاهزة (the_tags(), the_category(), the_author_posts_link()) بدل كتابة دوال منخفضة المستوى (get_the_terms(), إلخ) إذا لم تكن بحاجة للتحكم الكامل.
  • اربط الأكواد المتكررة في Template Parts: مثل get_template_part( 'template-parts/content', 'single' ); لسهولة إعادة الاستخدام.

✨ القاعدة العامة لا ستخدام Echo مع الدوال

  • دوال تبدأ بـ the_
    → هذه الدوال تطبع (echo) القيمة مباشرة في مكانها.
    ✔️ لا تحتاج أن تكتب قبلها echo.
    ❌ لو كتبت echo the_title(); مثلًا، ستطبع العنوان مرتين (مرة من داخل the_title() ومرة لأنك طلبت من echo طباعة القيمة المرجعة، وهي غالبًا null).
  • دوال تبدأ بـ get_
    → هذه الدوال ترجع (return) القيمة كسلسلة/مصفوفة بدون أن تطبعها.
    ✔️ تحتاج تستخدم معها echo إذا أردت إظهار النتيجة على الشاشة.
📝 أمثلة عملية
1. عنوان البوست
the_title(); // تطبع مباشرة
echo get_the_title(); // نفس النتيجة لكن هنا انت اللي طلبت الطباعة
PHP
  • الأفضلية:
    • لو تحتاج بس تعرض العنوان → استخدم the_title().
    • لو تحتاج تعديل العنوان قبل عرضه (مثلاً إضافة كلمة قبلها) → استخدم get_the_title() ثم echo.
2. الرابط (permalink)
echo get_permalink(); // لازم echo لأنه يرجع نص فقط
// the_permalink(); // تطبع مباشرة (بدون echo)
PHP
3. الوسوم
the_tags(); // تطبع الوسوم كرابط
$tags = get_the_tags();
if ( $tags ) {
   foreach( $tags as $tag ) {
      echo esc_html( $tag->name );
   }
}
PHP
4. الصورة البارزة
the_post_thumbnail(); // تطبع <img>
echo get_the_post_thumbnail( get_the_ID(), 'medium' ); // ترجع <img> كسلسلة
PHP
⚡ نصائح سريعة
  1. عند الطباعة المباشرة: استخدم the_ لأنها أنظف وأبسط.
  2. عند المعالجة/التعقيم/إعادة الاستخدام: استخدم get_.
    • مثال: esc_html( get_the_title() ) أفضل من الاعتماد على the_title() إذا تريد أمان أكثر.
  3. إذا كنت تشك: تذكر أن the_ غالبًا “تطبع”، و get_ غالبًا “ترجع”.

🎯 خلاصة:

  • بدون echo → مع دوال the_ (لأنها تطبع وحدها).
  • مع echo → مع دوال get_ (لأنها ترجع القيمة فقط).