Created
November 18, 2011 17:11
-
-
Save johnpbloch/1377081 to your computer and use it in GitHub Desktop.
Hijacking the main WordPress query and loop for fun and profit
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/* | |
* Plugin Name: Query Thief | |
* Description: Hijacking the main WordPress query and loop for fun and profit | |
* Version: 0.1 | |
*/ | |
// I namespace everything now that PHP 5.2.4 is the minimum requirement. :D | |
namespace JPB; | |
/* | |
For my purposes, I needed to hijack the query and loop to disable the default | |
search functionality on the front end, and replace it with a 3rd party search | |
provider; that's why everything here has to do with searching. If you use this, | |
make sure to get rid of those and replace them with what you need. | |
This is a terrible terrible thing to do if you don't understand what it's doing, | |
and UNLESS you actively follow WordPress core development and WILL notice when | |
the code that this is based on WILL change, DO NOT TRY THIS AT HOME. | |
Also, I refactored this from a larger plugin I built that also does the calling | |
and parsing of the third party's search materials, and have not tested this code | |
in production. It may fail extra spectacularly, for all I know. | |
This is without any warranty or guarantee of support, explicit or implicit. Use | |
at your own risk. Preferably for profit. Or fun. Heck, or both. | |
*/ | |
function init(){ | |
// We don't want to run this anywhere but on the home page. | |
if( is_admin() ) | |
return; | |
add_action( 'parse_request', '\\JPB\\hijack' ); | |
add_action( 'loop_start', '\\JPB\\loop_start' ); | |
add_action( 'loop_end', '\\JPB\\loop_end' ); | |
} | |
add_action( 'init', '\\JPB\\init' ); | |
function hijack( &$wp ){ | |
// I'm only interested in searches. Mmmmm, delicious searches... | |
if( !isset( $wp->query_vars['s'] ) ) | |
return; | |
// We need the global query--the one that backs up the normal one, so | |
// our changes will always stay where we put them | |
global $wp_the_query; | |
// The following two lines continue the execution of WP::main(). (We're | |
// running at the tail end of WP::parse_request() right now) | |
$wp->send_headers(); | |
$wp->build_query_string(); | |
// Rather than call WP::query_posts(), we'll add the important parts of | |
// its procedures here too. | |
// initialize it: | |
$wp_the_query->init(); | |
// Fill the query variables: | |
$wp_the_query->query = $wp_the_query->query_vars = wp_parse_args( $wp->query_vars ); | |
// For my purposes, I needed it to look like a search. You may need it | |
// to be a taxonomy or something else. It's cool, the query will do | |
// whatever you want it to. Within reason. | |
$wp_the_query->is_search = true; | |
// VERY IMPORTANT PART | |
// Set a unique property on the query object; something that core would | |
// never put there, and only this function would, to an empty value. | |
$wp_the_query->jpb_search_object = 0; | |
// Set the post count to one | |
$wp_the_query->post_count = 1; | |
// Create a fake post. | |
$wp_the_query->posts = array( 0 => (object)array( | |
'post_content' => '', | |
'post_excerpt' => '', | |
'post_status' => 'publish', | |
'comment_status' => '', | |
'ping_status' => '', | |
'ID' => 0, | |
'post_author' => '0', | |
'post_date' => '0000-00-00 00:00:00', | |
'post_title' => '', | |
'post_name' => '', | |
'post_date_gmt' => '0000-00-00 00:00:00', | |
'post_password' => '', | |
'to_ping' => '', | |
'pinged' => '', | |
'post_modified' => '0000-00-00 00:00:00', | |
'post_modified_gmt' => '0000-00-00 00:00:00', | |
'post_content_filtered' => '', | |
'post_parent' => 0, | |
'guid' => '', | |
'menu_order' => 0, | |
'post_type' => 'page', | |
'post_mime_type' => '', | |
'comment_count' => '0', | |
) ); | |
// Tell the query that it's ready to start | |
$wp_the_query->current_post = -1; | |
// include the template loader ourselves | |
include( ABSPATH . WPINC . '/template-loader.php' ); | |
// Exit before WordPress can REALLY finish the query and template | |
// loading process. | |
exit; | |
} | |
function loop_start( &$wp ){ | |
// If we don't have our unique flag, get out of here. We're in the | |
// wrong loop. | |
if( !isset( $wp->jpb_search_object ) ) | |
return; | |
// Set the unique flag to a non-empty value | |
$wp->jpb_search_object = 1; | |
/******************************************| | |
| | | |
| PROFIT | | |
| | | |
|******************************************/ | |
// Turn on an output buffer with a custom filter | |
// This will catch any output that may accidentally slip through | |
// in the middle of the loop, due to hardcoded html in the template. | |
// Use this at your own discretion. | |
ob_start( '\\JPB\\ob_filter' ); | |
} | |
function ob_filter( $buffer ){ | |
global $wp; | |
// Don't return anything until we're done with the loop. | |
if( !empty( $wp->jpb_search_object ) ) | |
return ''; | |
// If we are done with the loop, send the buffer back. | |
return $buffer; | |
} | |
function loop_end( &$wp ){ | |
// If we don't have our unique flag, get out of here. We're in the | |
// wrong loop. | |
if( empty( $wp->jpb_search_object ) ) | |
return; | |
// First let's turn off output buffering | |
ob_end_clean(); | |
// We're in the main loop, at the end of it. Let's unset the flag: | |
unset( $wp->jpb_search_object ); | |
// All done! | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment