3

I'm creating a plugin for a friend to log search terms on his site. It's very basic. It creates a new table to store terms entered in the search box. It converts the search term to lower case, checks the database for a matching term and either inserts the term into the database or increases the count of a matching term. It was working as expected on my hosting account. Then I installed it on a WP install on a GoDaddy hosting account...

As long as I'm logged in it seems to work whether or not there is a matching term in the database. When I search the site as a guest, it will add a new unique search term to the database but if there is an existing term it does nothing. Well, that's not entirely true. I've seen it increase the count a few times but not by 1, by 2. It seems to function very sporadically on GoDaddy. I can't make sense of this. The actual plugin can be found here.

I appreciate any time spent looking at this.

Thanks

// Register function to be called when plugin is activated
register_activation_hook( __FILE__, 'mac_search_log_activation' );

function mac_search_log_activation(){
    global $wpdb;
    $prefix = $wpdb->prefix;
    $creation_query =
        'CREATE TABLE ' . $prefix . 'mac_search_log (
            `id` mediumint(8) NOT NULL AUTO_INCREMENT,
            `full_search_term` text NOT NULL,
            `count` mediumint(8) NOT NULL,
            PRIMARY KEY (`id`)
            );';

    require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
    dbDelta( $creation_query );
}

// Register function to be called when administration pages init takes place
add_action( 'admin_init', 'mac_search_log_admin_init' );

function mac_search_log_admin_init(){
    add_action('admin_post_delete_mac_search_log_term', 'delete_mac_search_log_term');
}

// Register function to be called when admin menu is constructed
add_action( 'admin_menu', 'mac_search_log_settings_menu' );

// Add new menu item for Search Term History
function mac_search_log_settings_menu() {
    add_menu_page( 'Search Term History',
        'Search Term History', 'manage_options',
        'search-term-history',
        'mac_search_log_config_page',
        null,
        4.7);
}

// Function to render plugin admin page
function mac_search_log_config_page() {
    global $wpdb;
    ?>

    <!-- Top-level menu -->
    <div id="mac_search_log-general" class="wrap">
        <h2>Searched Terms</h2>

        <form method="post"
              action="<?php echo admin_url( 'admin-post.php' ); ?>">
            <input type="hidden" name="action" value="delete_mac_search_log_term" />
            <?php wp_nonce_field( 'mac_search_log_term_deletion' ); ?>

        <?php
        $term_query = 'select * from ';
        $term_query .= $wpdb->prefix;
        $term_query .= 'mac_search_log ORDER by count DESC';

        $term_items = $wpdb->get_results( $wpdb->prepare( $term_query ), ARRAY_A );
        ?>

       <table class="wp-list-table widefat fixed" >
            <thead><tr>
                <th style="width: 50px">Delete</th><th style="width: 300px">Search Term</th><th>Count</th></tr></thead>

            <?php
            // Display search terms if query returned results
            if ( $term_items ) {
                foreach ( $term_items as $term_item ) {
                    echo '<tr style="background: #FFF">';

                    echo '<td><input type="checkbox" name="terms[]" value="';
                    echo esc_attr( $term_item['id'] ) . '" /></td>';

                    echo '<td>' . $term_item['full_search_term'] . '</td>';;
                    echo '<td>' . $term_item['count'] . '</td></tr>';
                }
            } else {
                echo '<tr style="background: #FFF">';
                echo '<td colspan=4>No Searches Yet</td></tr>';
            }
            ?>
        </table><br />

            <input type="submit" value="Delete Selected"
                   class="button-primary"/>
        </form>


    </div>
    <?php }

    add_filter('pre_get_posts','MACSearchLog');

    function MACSearchLog($query) {
        if ($query->is_search) {
            global $wpdb;
            $term = strtolower($query->query_vars['s']);

            $term_query = 'select * from ';
            $term_query .= $wpdb->prefix;
            $term_query .= 'mac_search_log';
            $term_query .= ' WHERE full_search_term = "'.$term.'"';

            $term_items = $wpdb->get_row( $wpdb->prepare( $term_query ), ARRAY_A );

            if(empty($term_items)){
                // insert term and set count to 1
                $term_item = array(
                    'full_search_term' => $term,
                    'count' => 1
                );
                $wpdb->insert($wpdb->prefix.'mac_search_log', $term_item);

            }else{
                // update existing row count++
                $new_query = 'UPDATE ';
                $new_query .= $wpdb->prefix;
                $new_query .= 'mac_search_log';
                $new_query .= ' SET count = count+1 ';
                $new_query .= 'WHERE id = '.$term_items['id'];
                $wpdb->query($wpdb->prepare($new_query));
            }

        }

    }

    function delete_mac_search_log_term(){
        $deleted_terms = array();
        // Check that user has proper security level
        if ( !current_user_can( 'manage_options' ) )
            wp_die( 'Not allowed' );
        // Check if nonce field is present
        check_admin_referer( 'mac_search_log_term_deletion' );
        // If search terms are present, cycle through array and call SQL
        // command to delete entries one by one
        if ( !empty( $_POST['terms'] ) ) {
        // Retrieve array of search term IDs to be deleted
            $terms_to_delete = $_POST['terms'];
            global $wpdb;
            foreach ( $terms_to_delete as $term_to_delete ) {
                $deleted_terms[] = get_search_term_by_id(intval($term_to_delete));// TO GET SEARCH TERMS FOR EMAIL
                $query = 'DELETE from ' . $wpdb->prefix;
                $query .= 'mac_search_log ';
                $query .= 'WHERE id = ' .
                    intval( $term_to_delete );
                $wpdb->query( $wpdb->prepare( $query ) );
            }
        }
        // UNCOMMENT THE FOLLOWING 2 LINES TO DISPATCH EMAIL WITH DELETED SEARCH TERMS
    //    $terms = print_r($deleted_terms, true);
    //    wp_mail( get_option('admin_email'), 'Search Terms Deleted', $terms);

        // Reload the search term history page
        wp_redirect( add_query_arg( 'page', 'search-term-history',
            admin_url( 'admin.php' ) ) );
        exit;
    }

    function get_search_term_by_id($id){
        global $wpdb;
        $term_query = 'select * from ';
        $term_query .= $wpdb->prefix;
        $term_query .= 'mac_search_log ';
        $term_query .= 'WHERE `id` = '.$id;

        $term_item = $wpdb->get_row( $wpdb->prepare( $term_query ), OBJECT );

        if($term_item){
                return $term_item->full_search_term;
        }else{
            return null;
        }
    }

Edit: I've been logging queries and everything else involved. Nothing looks off. I replaced the SQL query with $wpdb->update() and replaced if(empty($term_items)){ with if($term_items) while swapping the associated actions, and that seems to have cleared up some of the anomolies with the count, but it just will not update on the GoDaddy account unless I am logged in. On one hand I find it hard to believe that a different hosting account would have an effect on this, but I've seen stranger things from GoDaddy in the past.

6
  • just from a quick glance I can see that prepare isn't being used correctly. it's probably not a reason things would be misbehaving, but it's definitely something that should be fixed. see this post for an explanation.
    – Milo
    Commented Jan 27, 2013 at 5:52
  • @Milo Thanks for the heads up on that. I was unaware it worked like printf() I made that change and now on GoDaddy it will update once when searching as a guest, but does nothing after the first update. Still works as expected on HostGator. Guess this is one step closer to solving the problem.
    – Mark
    Commented Jan 27, 2013 at 8:16
  • @Milo I guess the single update as Guest was an anomaly, it's not updating at all as Guest today on GoDaddy. I had a question about prepare(). If I change the first query in the above code to use %s for table name then use ->prepare($term_query, $table_name) it puts backticks around the table name and I get no results. Any idea why that would be? If I include the table name in the query and use ->prepare($term_query) there are no backticks around the table name and it returns all my rows.
    – Mark
    Commented Jan 27, 2013 at 20:58
  • 4
    On a side note, I called GoDaddy with the slim hope they might have a suggestion. After a few minutes, the guy came back and asked if I had posted something about this recently on StackExchange. Nice tech support, GoDaddy.
    – Mark
    Commented Jan 28, 2013 at 11:07
  • Unfortunately if issue is determined to be host-specific, it cannot be WordPress-specific. Shake hosting support [edited out me snapping :) ].
    – Rarst
    Commented May 27, 2013 at 13:22

1 Answer 1

2

I was able to recreate this, but it was a CDN (edge cache) and/or browser cache in my configuration that was bypassing your query.

For the browser cache to be a problem, the searches had to occur as the result of a link click. For example, if you clicked a link to ?s=term, it would check the browser cache first. If this occurred as the result of a form GET then it should bypass the browser cache, but I may be wrong about that.

To test this: check your .htaccess file for caching rules. Go Daddy uses browser caching rules to improve performance and compatibility with CDNs.

For the CDN / edge cache to be a problem, you would have to be a visitor (no cookies). CDNs cache pages along with meta information (i.e. how the page "varies") and this is usually the user agent, language, and some other things.

Go Daddy's ultimate hosting plans come with a CDN feature (which is what I tested with), but this may also happen with cloudflare or another CDN, too.

To test this: Visit your site in a different browser or modify your hosts file to use your hosting server's IP address instead of your CDN's ip address.

For a solution, I personally recommend using ajax. You cannot prevent some layer from caching the rendered HTML output of a search page, but you can embed an ajax callback to log the search string.

I hope this helps!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.