Plugin for showing cross-sells on the single product page in WooCommerce I made a custom plugin for showing cross-sells on the single product page in WooCommerce, however i need to know if this is secure and possibly act as an attack vector. Additionally, is there a better way than a while loop for iterating through the products?

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

if ( in_array( 'woocommerce/woocommerce.php', apply_filters( 'active_plugins', get_option( 'active_plugins' ) ) ) ) {

function show_cross_sell_in_single_product(){
    $crosssells = get_post_meta( get_the_ID(), '_crosssell_ids',true);
    $args = array(
        'post_type' =&gt; 'product',
        'posts_per_page' =&gt; 4,
        'post__in' =&gt; $crosssells
        );
    $products = new WP_Query( $args );
    if ( $products ) : ?&gt;

        &lt;section class="related products"&gt;

          &lt;h2&gt;&lt;?php esc_html_e( 'Passend dazu', 'woocommerce' ); ?&gt;&lt;/h2&gt;

          &lt;?php woocommerce_product_loop_start(); ?&gt;

          &lt;?php while ( $products-&gt;have_posts() ) : $products-&gt;the_post();
              wc_get_template_part( 'content', 'product' );
          endwhile; // end of the loop. ?&gt;

          &lt;?php woocommerce_product_loop_end(); ?&gt;

        &lt;/section&gt;

      &lt;?php endif;

    wp_reset_postdata();

}

add_action('woocommerce_after_single_product_summary', 'show_cross_sell_in_single_product', 10, 2);
/* todo removeaction woocommerce_after_single_product_summary, show_related */
}
The code looks ok to me from a security point of view. You're not accessing the database directly and it looks like you are escaping the output ( assuming that the template part does the same ).

A couple of suggestions:

Instead of looking for the filename in the list of active plugins, use function_exists or class_exists to check to see if the plugin is enabled by looking for a function or class defined by the plugin. The reason being that options can be cached and filenames can be changed. Function/Class names rarely change and it's much cleaner looking.

if ( class_exists( 'WC_Query' ) ) {
    // Your plugin code.
}


Check to make sure that you're actually getting data returned in $crosssells = get_post_meta( get_the_ID(), '_crosssell_ids',true); before using it in the query. If there is no data, there's no point in querying the database.

// Put this before the query
$crosssells = get_post_meta( get_the_ID(), '_crosssell_ids',true);    
if ( empty( $crosssells ) {
    return;
}


WP_Query will always return something and so if( $products ) will always be true. Use if( $products-&gt;have_posts() ) instead. This will only return true if there were posts returned from the query.

To answer your question, the while loop is fine. That's what WordPress core and the bundled theme use.

The only other suggestion I would make is for performance. Add a couple of items to your query args:

    $args = array(
        'post_type' =&gt; 'product',
        'posts_per_page' =&gt; 4,
        'post__in' =&gt; $crosssells,

        /**
         * If you're not paginating, this skips MYSQL_CALC statments
         */
        'no_found_rows' =&gt; true,

        /**
         * Skips updating meta cache
         */
        'update_post_meta_cache' =&gt; false,

        /**
         * Skips updating term cache.
         */            
         'update_post_term_cache' =&gt; false,
    );


Hope it helps!