web-dev-qa-db-fra.com

Comment résoudre un problème de mémoire suspect dans une boucle WordPress personnalisée?

J'ai écrit un plugin qui ajoute un "Où avez-vous entendu parler de nous?" question à la page de paiement WooCommerce et affiche les résultats (sous forme de graphique à barres horizontales simples) dans une page wp-admin.

Le plugin a bien fonctionné mais a cessé de fonctionner depuis que je l'ai écrit - apparemment, à cause d'un problème de mémoire.

Dans mon fichier wp-config.php, j'ai ... define('WP_MEMORY_LIMIT', '128M'); ... et j'ai aussi essayé 256MB, 512MB et 1G - mais aucun de ceux-ci ne résout le problème.

phpinfo() rapporte un memory_limit de 1G - donc cela semble bien.

Voici donc la fonction pour mon plugin qui est l'endroit où PHP semble abandonner:

function get_customer_sources() {

    $args = array(
        'post_type' => 'shop_order',
        'post_status' => 'publish',
        'posts_per_page' => -1,
        'tax_query' => array(
            array(
                'taxonomy' => 'shop_order_status',
                'field' => 'slug',
                'terms' => array('processing', 'completed', 'cancelled', 'failed', 'refunded')
            )
        )
    );

    //$custom_posts = new WP_Query($args);
    global $post; // required
    $custom_posts = get_posts($args);

    $all_sources = array(); //The return array
    echo "<h1>START</h1>";

    //while ($custom_posts->have_posts()) : $custom_posts->the_post();
    //if ( $custom_posts->have_posts() ) : while ( $custom_posts->have_posts() ) : $custom_posts->the_post();
    foreach($custom_posts as $post) : setup_postdata($post);


        //echo "<h2>A</h2>";

        $order_id = $post->ID;

        //echo "<h3>Order ID: $order_id</h3>";

        if ( get_post_meta($order_id, 'Where did you hear about us', true) ) {

            $source = get_post_meta($order_id, 'Where did you hear about us', true);

            //echo '<h4>Order ID: ' . $order_id . ': ' . $source . '</h4>';

            // Fix the "Vegan event" vs "Vegan Event" problem
            $source = ucfirst(strtolower($source));

            // Add the source to the return array
            if (array_key_exists($source, $all_sources)) {
                // Increment existing value
                $num = $all_sources[$source];
                $all_sources[$source] = $num + 1;
            }
            else {
                // Add value
                $all_sources[$source] = 1;
            }

            //echo '<h4>Num sources: ' . count($all_sources) . '</h4>';

        }

        //echo "<h2>B</h2>";

    //endwhile;
    //endwhile; endif;
    endforeach;

    echo "<h1>END</h1>";

    return $all_sources;
}`

Je pensais que peut-être il y avait quelques mauvaises données qui gâchaient les choses, alors j'ai changé le ...

if ( get_post_meta($order_id, 'Where did you hear about us', true) ) {

... ligne à ...

if ( $order_id < 5000 && get_post_meta($order_id, 'Where did you hear about us', true) ) {

... et ça a bien fonctionné.

Ensuite, j'ai changé cette ligne en ...

if ( $order_id >= 5000 && get_post_meta($order_id, 'Where did you hear about us', true) ) {

... et cela fonctionnait toujours.

Mais sans la condition 5000, le script explose. (Je le sais parce que <h1>END</h1> n'est pas sorti.)

Alors, qu'est-ce qui ne va pas avec mon code et/ou comment puis-je résoudre ce problème de non-achèvement de la méthode? Par exemple, pourrait-il être écrit plus efficacement en termes de mémoire? Ou y a-t-il une meilleure approche? (D'après les commentaires, j'ai déjà essayé d'utiliser WP_Query, mais j'obtiens les mêmes résultats.)

NB - Il y a environ. 10 000 éléments renvoyés par la requête.

Merci d'avance.

1

Il y a environ. 10 000 éléments renvoyés par la requête.

C'est ton problème là. Quoi que vous fassiez dans la boucle, WordPress charge toujours 10 000 objets postés en mémoire.

Batch it up et saupoudrer un peu de magie dans vos arguments de requête:

$args = array(
    'fields'         => 'ids', // MAGIC! Just get an array of id's, no objects committed to memory
    'posts_per_page' => 500,   // Batch size: big enough to reduce meta queries, small enough not to hammer memory
    'post_type'      => 'shop_order',
    'post_status'    => 'publish',
    'tax_query'      => array(
        array(
            'taxonomy' => 'shop_order_status',
            'field' => 'slug',
            'terms' => array( 'processing', 'completed', 'cancelled', 'failed', 'refunded' ),
        )
    )
);

$query = new WP_Query;
$paged = 1;
$count = 0;
$total = null;

do {
    $args['no_found_rows'] = isset( $total ); // No need to SQL_CALC_FOUND_ROWS on subsequent iterations
    $args['paged'] = $paged++;

    $post_ids = $query->query( $args );
    update_postmeta_cache( $post_ids ); // Get all meta for this group of posts

    if ( ! isset( $total ) )
        $total = $query->found_posts;

    $count += $query->post_count;

    foreach ( $post_ids as $post_id ) {
        $source = get_post_meta( $post_id, 'Where did you hear about us', true );

        // Do your stuff here



        // Wipe this post's meta from memory
        wp_cache_delete( $post_id, 'post_meta' );
    }

} while ( $count < $total );
4
TheDeadMedic