Changeset 16051
- Timestamp:
- 10/28/2010 04:27:18 PM (14 years ago)
- Location:
- trunk
- Files:
-
- 2 edited
- 1 copied
Legend:
- Unmodified
- Added
- Removed
-
trunk/wp-includes/class.wp-object-query.php
r16048 r16051 10 10 * @package WordPress 11 11 */ 12 13 /**14 * WordPress environment setup class.15 *16 * @package WordPress17 * @since 2.0.018 */19 class WP {20 /**21 * Public query variables.22 *23 * Long list of public query variables.24 *25 * @since 2.0.026 * @access public27 * @var array28 */29 var $public_query_vars = array('m', 'p', 'posts', 'w', 'cat', 'withcomments', 'withoutcomments', 's', 'search', 'exact', 'sentence', 'debug', 'calendar', 'page', 'paged', 'more', 'tb', 'pb', 'author', 'order', 'orderby', 'year', 'monthnum', 'day', 'hour', 'minute', 'second', 'name', 'category_name', 'tag', 'feed', 'author_name', 'static', 'pagename', 'page_id', 'error', 'comments_popup', 'attachment', 'attachment_id', 'subpost', 'subpost_id', 'preview', 'robots', 'taxonomy', 'term', 'cpage', 'post_type');30 31 /**32 * Private query variables.33 *34 * Long list of private query variables.35 *36 * @since 2.0.037 * @var array38 */39 var $private_query_vars = array('offset', 'posts_per_page', 'posts_per_archive_page', 'showposts', 'nopaging', 'post_type', 'post_status', 'category__in', 'category__not_in', 'category__and', 'tag__in', 'tag__not_in', 'tag__and', 'tag_slug__in', 'tag_slug__and', 'tag_id', 'post_mime_type', 'perm', 'comments_per_page', 'post__in', 'post__not_in');40 41 /**42 * Extra query variables set by the user.43 *44 * @since 2.1.045 * @var array46 */47 var $extra_query_vars = array();48 49 /**50 * Query variables for setting up the WordPress Query Loop.51 *52 * @since 2.0.053 * @var array54 */55 var $query_vars;56 57 /**58 * String parsed to set the query variables.59 *60 * @since 2.0.061 * @var string62 */63 var $query_string;64 65 /**66 * Permalink or requested URI.67 *68 * @since 2.0.069 * @var string70 */71 var $request;72 73 /**74 * Rewrite rule the request matched.75 *76 * @since 2.0.077 * @var string78 */79 var $matched_rule;80 81 /**82 * Rewrite query the request matched.83 *84 * @since 2.0.085 * @var string86 */87 var $matched_query;88 89 /**90 * Whether already did the permalink.91 *92 * @since 2.0.093 * @var bool94 */95 var $did_permalink = false;96 97 /**98 * Add name to list of public query variables.99 *100 * @since 2.1.0101 *102 * @param string $qv Query variable name.103 */104 function add_query_var($qv) {105 if ( !in_array($qv, $this->public_query_vars) )106 $this->public_query_vars[] = $qv;107 }108 109 /**110 * Set the value of a query variable.111 *112 * @since 2.3.0113 *114 * @param string $key Query variable name.115 * @param mixed $value Query variable value.116 */117 function set_query_var($key, $value) {118 $this->query_vars[$key] = $value;119 }120 121 /**122 * Parse request to find correct WordPress query.123 *124 * Sets up the query variables based on the request. There are also many125 * filters and actions that can be used to further manipulate the result.126 *127 * @since 2.0.0128 *129 * @param array|string $extra_query_vars Set the extra query variables.130 */131 function parse_request($extra_query_vars = '') {132 global $wp_rewrite;133 134 $this->query_vars = array();135 $post_type_query_vars = array();136 137 if ( is_array($extra_query_vars) )138 $this->extra_query_vars = & $extra_query_vars;139 else if (! empty($extra_query_vars))140 parse_str($extra_query_vars, $this->extra_query_vars);141 142 // Process PATH_INFO, REQUEST_URI, and 404 for permalinks.143 144 // Fetch the rewrite rules.145 $rewrite = $wp_rewrite->wp_rewrite_rules();146 147 if ( ! empty($rewrite) ) {148 // If we match a rewrite rule, this will be cleared.149 $error = '404';150 $this->did_permalink = true;151 152 if ( isset($_SERVER['PATH_INFO']) )153 $pathinfo = $_SERVER['PATH_INFO'];154 else155 $pathinfo = '';156 $pathinfo_array = explode('?', $pathinfo);157 $pathinfo = str_replace("%", "%25", $pathinfo_array[0]);158 $req_uri = $_SERVER['REQUEST_URI'];159 $req_uri_array = explode('?', $req_uri);160 $req_uri = $req_uri_array[0];161 $self = $_SERVER['PHP_SELF'];162 $home_path = parse_url(home_url());163 if ( isset($home_path['path']) )164 $home_path = $home_path['path'];165 else166 $home_path = '';167 $home_path = trim($home_path, '/');168 169 // Trim path info from the end and the leading home path from the170 // front. For path info requests, this leaves us with the requesting171 // filename, if any. For 404 requests, this leaves us with the172 // requested permalink.173 $req_uri = str_replace($pathinfo, '', $req_uri);174 $req_uri = trim($req_uri, '/');175 $req_uri = preg_replace("|^$home_path|", '', $req_uri);176 $req_uri = trim($req_uri, '/');177 $pathinfo = trim($pathinfo, '/');178 $pathinfo = preg_replace("|^$home_path|", '', $pathinfo);179 $pathinfo = trim($pathinfo, '/');180 $self = trim($self, '/');181 $self = preg_replace("|^$home_path|", '', $self);182 $self = trim($self, '/');183 184 // The requested permalink is in $pathinfo for path info requests and185 // $req_uri for other requests.186 if ( ! empty($pathinfo) && !preg_match('|^.*' . $wp_rewrite->index . '$|', $pathinfo) ) {187 $request = $pathinfo;188 } else {189 // If the request uri is the index, blank it out so that we don't try to match it against a rule.190 if ( $req_uri == $wp_rewrite->index )191 $req_uri = '';192 $request = $req_uri;193 }194 195 $this->request = $request;196 197 // Look for matches.198 $request_match = $request;199 foreach ( (array) $rewrite as $match => $query) {200 // Don't try to match against AtomPub calls201 if ( $req_uri == 'wp-app.php' )202 break;203 204 // If the requesting file is the anchor of the match, prepend it205 // to the path info.206 if ( (! empty($req_uri)) && (strpos($match, $req_uri) === 0) && ($req_uri != $request) )207 $request_match = $req_uri . '/' . $request;208 209 if ( preg_match("#^$match#", $request_match, $matches) ||210 preg_match("#^$match#", urldecode($request_match), $matches) ) {211 // Got a match.212 $this->matched_rule = $match;213 214 // Trim the query of everything up to the '?'.215 $query = preg_replace("!^.+\?!", '', $query);216 217 // Substitute the substring matches into the query.218 $query = addslashes(WP_MatchesMapRegex::apply($query, $matches));219 220 $this->matched_query = $query;221 222 // Parse the query.223 parse_str($query, $perma_query_vars);224 225 // If we're processing a 404 request, clear the error var226 // since we found something.227 if ( isset($_GET['error']) )228 unset($_GET['error']);229 230 if ( isset($error) )231 unset($error);232 233 break;234 }235 }236 237 // If req_uri is empty or if it is a request for ourself, unset error.238 if ( empty($request) || $req_uri == $self || strpos($_SERVER['PHP_SELF'], 'wp-admin/') !== false ) {239 if ( isset($_GET['error']) )240 unset($_GET['error']);241 242 if ( isset($error) )243 unset($error);244 245 if ( isset($perma_query_vars) && strpos($_SERVER['PHP_SELF'], 'wp-admin/') !== false )246 unset($perma_query_vars);247 248 $this->did_permalink = false;249 }250 }251 252 $this->public_query_vars = apply_filters('query_vars', $this->public_query_vars);253 254 foreach ( $GLOBALS['wp_post_types'] as $post_type => $t )255 if ( $t->query_var )256 $post_type_query_vars[$t->query_var] = $post_type;257 258 foreach ( $this->public_query_vars as $wpvar ) {259 if ( isset( $this->extra_query_vars[$wpvar] ) )260 $this->query_vars[$wpvar] = $this->extra_query_vars[$wpvar];261 elseif ( isset( $_POST[$wpvar] ) )262 $this->query_vars[$wpvar] = $_POST[$wpvar];263 elseif ( isset( $_GET[$wpvar] ) )264 $this->query_vars[$wpvar] = $_GET[$wpvar];265 elseif ( isset( $perma_query_vars[$wpvar] ) )266 $this->query_vars[$wpvar] = $perma_query_vars[$wpvar];267 268 if ( !empty( $this->query_vars[$wpvar] ) ) {269 if ( ! is_array( $this->query_vars[$wpvar] ) ) {270 $this->query_vars[$wpvar] = (string) $this->query_vars[$wpvar];271 } else {272 foreach ( $this->query_vars[$wpvar] as $vkey => $v ) {273 if ( !is_object( $v ) ) {274 $this->query_vars[$wpvar][$vkey] = (string) $v;275 }276 }277 }278 279 if ( isset($post_type_query_vars[$wpvar] ) ) {280 $this->query_vars['post_type'] = $post_type_query_vars[$wpvar];281 $this->query_vars['name'] = $this->query_vars[$wpvar];282 }283 }284 }285 286 // Limit publicly queried post_types to those that are publicly_queryable287 if ( isset( $this->query_vars['post_type']) ) {288 $queryable_post_types = get_post_types( array('publicly_queryable' => true) );289 if ( ! is_array( $this->query_vars['post_type'] ) ) {290 if ( ! in_array( $this->query_vars['post_type'], $queryable_post_types ) )291 unset( $this->query_vars['post_type'] );292 } else {293 $this->query_vars['post_type'] = array_intersect( $this->query_vars['post_type'], $queryable_post_types );294 }295 }296 297 foreach ( (array) $this->private_query_vars as $var) {298 if ( isset($this->extra_query_vars[$var]) )299 $this->query_vars[$var] = $this->extra_query_vars[$var];300 }301 302 if ( isset($error) )303 $this->query_vars['error'] = $error;304 305 $this->query_vars = apply_filters('request', $this->query_vars);306 307 do_action_ref_array('parse_request', array(&$this));308 }309 310 /**311 * Send additional HTTP headers for caching, content type, etc.312 *313 * Sets the X-Pingback header, 404 status (if 404), Content-type. If showing314 * a feed, it will also send last-modified, etag, and 304 status if needed.315 *316 * @since 2.0.0317 */318 function send_headers() {319 $headers = array('X-Pingback' => get_bloginfo('pingback_url'));320 $status = null;321 $exit_required = false;322 323 if ( is_user_logged_in() )324 $headers = array_merge($headers, wp_get_nocache_headers());325 if ( !empty($this->query_vars['error']) && '404' == $this->query_vars['error'] ) {326 $status = 404;327 if ( !is_user_logged_in() )328 $headers = array_merge($headers, wp_get_nocache_headers());329 $headers['Content-Type'] = get_option('html_type') . '; charset=' . get_option('blog_charset');330 } else if ( empty($this->query_vars['feed']) ) {331 $headers['Content-Type'] = get_option('html_type') . '; charset=' . get_option('blog_charset');332 } else {333 // We're showing a feed, so WP is indeed the only thing that last changed334 if ( !empty($this->query_vars['withcomments'])335 || ( empty($this->query_vars['withoutcomments'])336 && ( !empty($this->query_vars['p'])337 || !empty($this->query_vars['name'])338 || !empty($this->query_vars['page_id'])339 || !empty($this->query_vars['pagename'])340 || !empty($this->query_vars['attachment'])341 || !empty($this->query_vars['attachment_id'])342 )343 )344 )345 $wp_last_modified = mysql2date('D, d M Y H:i:s', get_lastcommentmodified('GMT'), 0).' GMT';346 else347 $wp_last_modified = mysql2date('D, d M Y H:i:s', get_lastpostmodified('GMT'), 0).' GMT';348 $wp_etag = '"' . md5($wp_last_modified) . '"';349 $headers['Last-Modified'] = $wp_last_modified;350 $headers['ETag'] = $wp_etag;351 352 // Support for Conditional GET353 if (isset($_SERVER['HTTP_IF_NONE_MATCH']))354 $client_etag = stripslashes(stripslashes($_SERVER['HTTP_IF_NONE_MATCH']));355 else $client_etag = false;356 357 $client_last_modified = empty($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? '' : trim($_SERVER['HTTP_IF_MODIFIED_SINCE']);358 // If string is empty, return 0. If not, attempt to parse into a timestamp359 $client_modified_timestamp = $client_last_modified ? strtotime($client_last_modified) : 0;360 361 // Make a timestamp for our most recent modification...362 $wp_modified_timestamp = strtotime($wp_last_modified);363 364 if ( ($client_last_modified && $client_etag) ?365 (($client_modified_timestamp >= $wp_modified_timestamp) && ($client_etag == $wp_etag)) :366 (($client_modified_timestamp >= $wp_modified_timestamp) || ($client_etag == $wp_etag)) ) {367 $status = 304;368 $exit_required = true;369 }370 }371 372 $headers = apply_filters('wp_headers', $headers, $this);373 374 if ( ! empty( $status ) )375 status_header( $status );376 foreach( (array) $headers as $name => $field_value )377 @header("{$name}: {$field_value}");378 379 if ( $exit_required )380 exit();381 382 do_action_ref_array('send_headers', array(&$this));383 }384 385 /**386 * Sets the query string property based off of the query variable property.387 *388 * The 'query_string' filter is deprecated, but still works. Plugins should389 * use the 'request' filter instead.390 *391 * @since 2.0.0392 */393 function build_query_string() {394 $this->query_string = '';395 foreach ( (array) array_keys($this->query_vars) as $wpvar) {396 if ( '' != $this->query_vars[$wpvar] ) {397 $this->query_string .= (strlen($this->query_string) < 1) ? '' : '&';398 if ( !is_scalar($this->query_vars[$wpvar]) ) // Discard non-scalars.399 continue;400 $this->query_string .= $wpvar . '=' . rawurlencode($this->query_vars[$wpvar]);401 }402 }403 404 // query_string filter deprecated. Use request filter instead.405 if ( has_filter('query_string') ) { // Don't bother filtering and parsing if no plugins are hooked in.406 $this->query_string = apply_filters('query_string', $this->query_string);407 parse_str($this->query_string, $this->query_vars);408 }409 }410 411 /**412 * Set up the WordPress Globals.413 *414 * The query_vars property will be extracted to the GLOBALS. So care should415 * be taken when naming global variables that might interfere with the416 * WordPress environment.417 *418 * @global string $query_string Query string for the loop.419 * @global int $more Only set, if single page or post.420 * @global int $single If single page or post. Only set, if single page or post.421 *422 * @since 2.0.0423 */424 function register_globals() {425 global $wp_query;426 // Extract updated query vars back into global namespace.427 foreach ( (array) $wp_query->query_vars as $key => $value) {428 $GLOBALS[$key] = $value;429 }430 431 $GLOBALS['query_string'] = $this->query_string;432 $GLOBALS['posts'] = & $wp_query->posts;433 $GLOBALS['post'] = (isset($wp_query->post)) ? $wp_query->post : null;434 $GLOBALS['request'] = $wp_query->request;435 436 if ( is_single() || is_page() ) {437 $GLOBALS['more'] = 1;438 $GLOBALS['single'] = 1;439 }440 }441 442 /**443 * Set up the current user.444 *445 * @since 2.0.0446 */447 function init() {448 wp_get_current_user();449 }450 451 /**452 * Set up the Loop based on the query variables.453 *454 * @uses WP::$query_vars455 * @since 2.0.0456 */457 function query_posts() {458 global $wp_the_query;459 $this->build_query_string();460 $wp_the_query->query($this->query_vars);461 }462 463 /**464 * Set the Headers for 404, if nothing is found for requested URL.465 *466 * Issue a 404 if a request doesn't match any posts and doesn't match467 * any object (e.g. an existing-but-empty category, tag, author) and a 404 was not already468 * issued, and if the request was not a search or the homepage.469 *470 * Otherwise, issue a 200.471 *472 * @since 2.0.0473 */474 function handle_404() {475 global $wp_query;476 477 if ( !is_admin() && ( 0 == count( $wp_query->posts ) ) && !is_404() && !is_robots() && !is_search() && !is_home() ) {478 // Don't 404 for these queries if they matched an object.479 if ( ( is_tag() || is_category() || is_tax() || is_author() ) && $wp_query->get_queried_object() && !is_paged() ) {480 if ( !is_404() )481 status_header( 200 );482 return;483 }484 $wp_query->set_404();485 status_header( 404 );486 nocache_headers();487 } elseif ( !is_404() ) {488 status_header( 200 );489 }490 }491 492 /**493 * Sets up all of the variables required by the WordPress environment.494 *495 * The action 'wp' has one parameter that references the WP object. It496 * allows for accessing the properties and methods to further manipulate the497 * object.498 *499 * @since 2.0.0500 *501 * @param string|array $query_args Passed to {@link parse_request()}502 */503 function main($query_args = '') {504 $this->init();505 $this->parse_request($query_args);506 $this->send_headers();507 $this->query_posts();508 $this->handle_404();509 $this->register_globals();510 do_action_ref_array('wp', array(&$this));511 }512 513 /**514 * PHP4 Constructor - Does nothing.515 *516 * Call main() method when ready to run setup.517 *518 * @since 2.0.0519 *520 * @return WP521 */522 function WP() {523 // Empty.524 }525 }526 12 527 13 /** … … 761 247 } 762 248 763 /**764 * WordPress Error class.765 *766 * Container for checking for WordPress errors and error messages. Return767 * WP_Error and use {@link is_wp_error()} to check if this class is returned.768 * Many core WordPress functions pass this class in the event of an error and769 * if not handled properly will result in code errors.770 *771 * @package WordPress772 * @since 2.1.0773 */774 class WP_Error {775 /**776 * Stores the list of errors.777 *778 * @since 2.1.0779 * @var array780 * @access private781 */782 var $errors = array();783 784 /**785 * Stores the list of data for error codes.786 *787 * @since 2.1.0788 * @var array789 * @access private790 */791 var $error_data = array();792 793 /**794 * PHP4 Constructor - Sets up error message.795 *796 * If code parameter is empty then nothing will be done. It is possible to797 * add multiple messages to the same code, but with other methods in the798 * class.799 *800 * All parameters are optional, but if the code parameter is set, then the801 * data parameter is optional.802 *803 * @since 2.1.0804 *805 * @param string|int $code Error code806 * @param string $message Error message807 * @param mixed $data Optional. Error data.808 * @return WP_Error809 */810 function WP_Error($code = '', $message = '', $data = '') {811 if ( empty($code) )812 return;813 814 $this->errors[$code][] = $message;815 816 if ( ! empty($data) )817 $this->error_data[$code] = $data;818 }819 820 /**821 * Retrieve all error codes.822 *823 * @since 2.1.0824 * @access public825 *826 * @return array List of error codes, if avaiable.827 */828 function get_error_codes() {829 if ( empty($this->errors) )830 return array();831 832 return array_keys($this->errors);833 }834 835 /**836 * Retrieve first error code available.837 *838 * @since 2.1.0839 * @access public840 *841 * @return string|int Empty string, if no error codes.842 */843 function get_error_code() {844 $codes = $this->get_error_codes();845 846 if ( empty($codes) )847 return '';848 849 return $codes[0];850 }851 852 /**853 * Retrieve all error messages or error messages matching code.854 *855 * @since 2.1.0856 *857 * @param string|int $code Optional. Retrieve messages matching code, if exists.858 * @return array Error strings on success, or empty array on failure (if using codee parameter).859 */860 function get_error_messages($code = '') {861 // Return all messages if no code specified.862 if ( empty($code) ) {863 $all_messages = array();864 foreach ( (array) $this->errors as $code => $messages )865 $all_messages = array_merge($all_messages, $messages);866 867 return $all_messages;868 }869 870 if ( isset($this->errors[$code]) )871 return $this->errors[$code];872 else873 return array();874 }875 876 /**877 * Get single error message.878 *879 * This will get the first message available for the code. If no code is880 * given then the first code available will be used.881 *882 * @since 2.1.0883 *884 * @param string|int $code Optional. Error code to retrieve message.885 * @return string886 */887 function get_error_message($code = '') {888 if ( empty($code) )889 $code = $this->get_error_code();890 $messages = $this->get_error_messages($code);891 if ( empty($messages) )892 return '';893 return $messages[0];894 }895 896 /**897 * Retrieve error data for error code.898 *899 * @since 2.1.0900 *901 * @param string|int $code Optional. Error code.902 * @return mixed Null, if no errors.903 */904 function get_error_data($code = '') {905 if ( empty($code) )906 $code = $this->get_error_code();907 908 if ( isset($this->error_data[$code]) )909 return $this->error_data[$code];910 return null;911 }912 913 /**914 * Append more error messages to list of error messages.915 *916 * @since 2.1.0917 * @access public918 *919 * @param string|int $code Error code.920 * @param string $message Error message.921 * @param mixed $data Optional. Error data.922 */923 function add($code, $message, $data = '') {924 $this->errors[$code][] = $message;925 if ( ! empty($data) )926 $this->error_data[$code] = $data;927 }928 929 /**930 * Add data for error code.931 *932 * The error code can only contain one error data.933 *934 * @since 2.1.0935 *936 * @param mixed $data Error data.937 * @param string|int $code Error code.938 */939 function add_data($data, $code = '') {940 if ( empty($code) )941 $code = $this->get_error_code();942 943 $this->error_data[$code] = $data;944 }945 }946 947 /**948 * Check whether variable is a WordPress Error.949 *950 * Looks at the object and if a WP_Error class. Does not check to see if the951 * parent is also WP_Error, so can't inherit WP_Error and still use this952 * function.953 *954 * @since 2.1.0955 *956 * @param mixed $thing Check if unknown variable is WordPress Error object.957 * @return bool True, if WP_Error. False, if not WP_Error.958 */959 function is_wp_error($thing) {960 if ( is_object($thing) && is_a($thing, 'WP_Error') )961 return true;962 return false;963 }964 965 /**966 * A class for displaying various tree-like structures.967 *968 * Extend the Walker class to use it, see examples at the below. Child classes969 * do not need to implement all of the abstract methods in the class. The child970 * only needs to implement the methods that are needed. Also, the methods are971 * not strictly abstract in that the parameter definition needs to be followed.972 * The child classes can have additional parameters.973 *974 * @package WordPress975 * @since 2.1.0976 * @abstract977 */978 class Walker {979 /**980 * What the class handles.981 *982 * @since 2.1.0983 * @var string984 * @access public985 */986 var $tree_type;987 988 /**989 * DB fields to use.990 *991 * @since 2.1.0992 * @var array993 * @access protected994 */995 var $db_fields;996 997 /**998 * Max number of pages walked by the paged walker999 *1000 * @since 2.7.01001 * @var int1002 * @access protected1003 */1004 var $max_pages = 1;1005 1006 /**1007 * Starts the list before the elements are added.1008 *1009 * Additional parameters are used in child classes. The args parameter holds1010 * additional values that may be used with the child class methods. This1011 * method is called at the start of the output list.1012 *1013 * @since 2.1.01014 * @abstract1015 *1016 * @param string $output Passed by reference. Used to append additional content.1017 */1018 function start_lvl(&$output) {}1019 1020 /**1021 * Ends the list of after the elements are added.1022 *1023 * Additional parameters are used in child classes. The args parameter holds1024 * additional values that may be used with the child class methods. This1025 * method finishes the list at the end of output of the elements.1026 *1027 * @since 2.1.01028 * @abstract1029 *1030 * @param string $output Passed by reference. Used to append additional content.1031 */1032 function end_lvl(&$output) {}1033 1034 /**1035 * Start the element output.1036 *1037 * Additional parameters are used in child classes. The args parameter holds1038 * additional values that may be used with the child class methods. Includes1039 * the element output also.1040 *1041 * @since 2.1.01042 * @abstract1043 *1044 * @param string $output Passed by reference. Used to append additional content.1045 */1046 function start_el(&$output) {}1047 1048 /**1049 * Ends the element output, if needed.1050 *1051 * Additional parameters are used in child classes. The args parameter holds1052 * additional values that may be used with the child class methods.1053 *1054 * @since 2.1.01055 * @abstract1056 *1057 * @param string $output Passed by reference. Used to append additional content.1058 */1059 function end_el(&$output) {}1060 1061 /**1062 * Traverse elements to create list from elements.1063 *1064 * Display one element if the element doesn't have any children otherwise,1065 * display the element and its children. Will only traverse up to the max1066 * depth and no ignore elements under that depth. It is possible to set the1067 * max depth to include all depths, see walk() method.1068 *1069 * This method shouldn't be called directly, use the walk() method instead.1070 *1071 * @since 2.5.01072 *1073 * @param object $element Data object1074 * @param array $children_elements List of elements to continue traversing.1075 * @param int $max_depth Max depth to traverse.1076 * @param int $depth Depth of current element.1077 * @param array $args1078 * @param string $output Passed by reference. Used to append additional content.1079 * @return null Null on failure with no changes to parameters.1080 */1081 function display_element( $element, &$children_elements, $max_depth, $depth=0, $args, &$output ) {1082 1083 if ( !$element )1084 return;1085 1086 $id_field = $this->db_fields['id'];1087 1088 //display this element1089 if ( is_array( $args[0] ) )1090 $args[0]['has_children'] = ! empty( $children_elements[$element->$id_field] );1091 $cb_args = array_merge( array(&$output, $element, $depth), $args);1092 call_user_func_array(array(&$this, 'start_el'), $cb_args);1093 1094 $id = $element->$id_field;1095 1096 // descend only when the depth is right and there are childrens for this element1097 if ( ($max_depth == 0 || $max_depth > $depth+1 ) && isset( $children_elements[$id]) ) {1098 1099 foreach( $children_elements[ $id ] as $child ){1100 1101 if ( !isset($newlevel) ) {1102 $newlevel = true;1103 //start the child delimiter1104 $cb_args = array_merge( array(&$output, $depth), $args);1105 call_user_func_array(array(&$this, 'start_lvl'), $cb_args);1106 }1107 $this->display_element( $child, $children_elements, $max_depth, $depth + 1, $args, $output );1108 }1109 unset( $children_elements[ $id ] );1110 }1111 1112 if ( isset($newlevel) && $newlevel ){1113 //end the child delimiter1114 $cb_args = array_merge( array(&$output, $depth), $args);1115 call_user_func_array(array(&$this, 'end_lvl'), $cb_args);1116 }1117 1118 //end this element1119 $cb_args = array_merge( array(&$output, $element, $depth), $args);1120 call_user_func_array(array(&$this, 'end_el'), $cb_args);1121 }1122 1123 /**1124 * Display array of elements hierarchically.1125 *1126 * It is a generic function which does not assume any existing order of1127 * elements. max_depth = -1 means flatly display every element. max_depth =1128 * 0 means display all levels. max_depth > 0 specifies the number of1129 * display levels.1130 *1131 * @since 2.1.01132 *1133 * @param array $elements1134 * @param int $max_depth1135 * @return string1136 */1137 function walk( $elements, $max_depth) {1138 1139 $args = array_slice(func_get_args(), 2);1140 $output = '';1141 1142 if ($max_depth < -1) //invalid parameter1143 return $output;1144 1145 if (empty($elements)) //nothing to walk1146 return $output;1147 1148 $id_field = $this->db_fields['id'];1149 $parent_field = $this->db_fields['parent'];1150 1151 // flat display1152 if ( -1 == $max_depth ) {1153 $empty_array = array();1154 foreach ( $elements as $e )1155 $this->display_element( $e, $empty_array, 1, 0, $args, $output );1156 return $output;1157 }1158 1159 /*1160 * need to display in hierarchical order1161 * separate elements into two buckets: top level and children elements1162 * children_elements is two dimensional array, eg.1163 * children_elements[10][] contains all sub-elements whose parent is 10.1164 */1165 $top_level_elements = array();1166 $children_elements = array();1167 foreach ( $elements as $e) {1168 if ( 0 == $e->$parent_field )1169 $top_level_elements[] = $e;1170 else1171 $children_elements[ $e->$parent_field ][] = $e;1172 }1173 1174 /*1175 * when none of the elements is top level1176 * assume the first one must be root of the sub elements1177 */1178 if ( empty($top_level_elements) ) {1179 1180 $first = array_slice( $elements, 0, 1 );1181 $root = $first[0];1182 1183 $top_level_elements = array();1184 $children_elements = array();1185 foreach ( $elements as $e) {1186 if ( $root->$parent_field == $e->$parent_field )1187 $top_level_elements[] = $e;1188 else1189 $children_elements[ $e->$parent_field ][] = $e;1190 }1191 }1192 1193 foreach ( $top_level_elements as $e )1194 $this->display_element( $e, $children_elements, $max_depth, 0, $args, $output );1195 1196 /*1197 * if we are displaying all levels, and remaining children_elements is not empty,1198 * then we got orphans, which should be displayed regardless1199 */1200 if ( ( $max_depth == 0 ) && count( $children_elements ) > 0 ) {1201 $empty_array = array();1202 foreach ( $children_elements as $orphans )1203 foreach( $orphans as $op )1204 $this->display_element( $op, $empty_array, 1, 0, $args, $output );1205 }1206 1207 return $output;1208 }1209 1210 /**1211 * paged_walk() - produce a page of nested elements1212 *1213 * Given an array of hierarchical elements, the maximum depth, a specific page number,1214 * and number of elements per page, this function first determines all top level root elements1215 * belonging to that page, then lists them and all of their children in hierarchical order.1216 *1217 * @package WordPress1218 * @since 2.71219 * @param int $max_depth = 0 means display all levels; $max_depth > 0 specifies the number of display levels.1220 * @param int $page_num the specific page number, beginning with 1.1221 * @return XHTML of the specified page of elements1222 */1223 function paged_walk( $elements, $max_depth, $page_num, $per_page ) {1224 1225 /* sanity check */1226 if ( empty($elements) || $max_depth < -1 )1227 return '';1228 1229 $args = array_slice( func_get_args(), 4 );1230 $output = '';1231 1232 $id_field = $this->db_fields['id'];1233 $parent_field = $this->db_fields['parent'];1234 1235 $count = -1;1236 if ( -1 == $max_depth )1237 $total_top = count( $elements );1238 if ( $page_num < 1 || $per_page < 0 ) {1239 // No paging1240 $paging = false;1241 $start = 0;1242 if ( -1 == $max_depth )1243 $end = $total_top;1244 $this->max_pages = 1;1245 } else {1246 $paging = true;1247 $start = ( (int)$page_num - 1 ) * (int)$per_page;1248 $end = $start + $per_page;1249 if ( -1 == $max_depth )1250 $this->max_pages = ceil($total_top / $per_page);1251 }1252 1253 // flat display1254 if ( -1 == $max_depth ) {1255 if ( !empty($args[0]['reverse_top_level']) ) {1256 $elements = array_reverse( $elements );1257 $oldstart = $start;1258 $start = $total_top - $end;1259 $end = $total_top - $oldstart;1260 }1261 1262 $empty_array = array();1263 foreach ( $elements as $e ) {1264 $count++;1265 if ( $count < $start )1266 continue;1267 if ( $count >= $end )1268 break;1269 $this->display_element( $e, $empty_array, 1, 0, $args, $output );1270 }1271 return $output;1272 }1273 1274 /*1275 * separate elements into two buckets: top level and children elements1276 * children_elements is two dimensional array, eg.1277 * children_elements[10][] contains all sub-elements whose parent is 10.1278 */1279 $top_level_elements = array();1280 $children_elements = array();1281 foreach ( $elements as $e) {1282 if ( 0 == $e->$parent_field )1283 $top_level_elements[] = $e;1284 else1285 $children_elements[ $e->$parent_field ][] = $e;1286 }1287 1288 $total_top = count( $top_level_elements );1289 if ( $paging )1290 $this->max_pages = ceil($total_top / $per_page);1291 else1292 $end = $total_top;1293 1294 if ( !empty($args[0]['reverse_top_level']) ) {1295 $top_level_elements = array_reverse( $top_level_elements );1296 $oldstart = $start;1297 $start = $total_top - $end;1298 $end = $total_top - $oldstart;1299 }1300 if ( !empty($args[0]['reverse_children']) ) {1301 foreach ( $children_elements as $parent => $children )1302 $children_elements[$parent] = array_reverse( $children );1303 }1304 1305 foreach ( $top_level_elements as $e ) {1306 $count++;1307 1308 //for the last page, need to unset earlier children in order to keep track of orphans1309 if ( $end >= $total_top && $count < $start )1310 $this->unset_children( $e, $children_elements );1311 1312 if ( $count < $start )1313 continue;1314 1315 if ( $count >= $end )1316 break;1317 1318 $this->display_element( $e, $children_elements, $max_depth, 0, $args, $output );1319 }1320 1321 if ( $end >= $total_top && count( $children_elements ) > 0 ) {1322 $empty_array = array();1323 foreach ( $children_elements as $orphans )1324 foreach( $orphans as $op )1325 $this->display_element( $op, $empty_array, 1, 0, $args, $output );1326 }1327 1328 return $output;1329 }1330 1331 function get_number_of_root_elements( $elements ){1332 1333 $num = 0;1334 $parent_field = $this->db_fields['parent'];1335 1336 foreach ( $elements as $e) {1337 if ( 0 == $e->$parent_field )1338 $num++;1339 }1340 return $num;1341 }1342 1343 // unset all the children for a given top level element1344 function unset_children( $e, &$children_elements ){1345 1346 if ( !$e || !$children_elements )1347 return;1348 1349 $id_field = $this->db_fields['id'];1350 $id = $e->$id_field;1351 1352 if ( !empty($children_elements[$id]) && is_array($children_elements[$id]) )1353 foreach ( (array) $children_elements[$id] as $child )1354 $this->unset_children( $child, $children_elements );1355 1356 if ( isset($children_elements[$id]) )1357 unset( $children_elements[$id] );1358 1359 }1360 }1361 1362 /**1363 * Create HTML list of pages.1364 *1365 * @package WordPress1366 * @since 2.1.01367 * @uses Walker1368 */1369 class Walker_Page extends Walker {1370 /**1371 * @see Walker::$tree_type1372 * @since 2.1.01373 * @var string1374 */1375 var $tree_type = 'page';1376 1377 /**1378 * @see Walker::$db_fields1379 * @since 2.1.01380 * @todo Decouple this.1381 * @var array1382 */1383 var $db_fields = array ('parent' => 'post_parent', 'id' => 'ID');1384 1385 /**1386 * @see Walker::start_lvl()1387 * @since 2.1.01388 *1389 * @param string $output Passed by reference. Used to append additional content.1390 * @param int $depth Depth of page. Used for padding.1391 */1392 function start_lvl(&$output, $depth) {1393 $indent = str_repeat("\t", $depth);1394 $output .= "\n$indent<ul class='children'>\n";1395 }1396 1397 /**1398 * @see Walker::end_lvl()1399 * @since 2.1.01400 *1401 * @param string $output Passed by reference. Used to append additional content.1402 * @param int $depth Depth of page. Used for padding.1403 */1404 function end_lvl(&$output, $depth) {1405 $indent = str_repeat("\t", $depth);1406 $output .= "$indent</ul>\n";1407 }1408 1409 /**1410 * @see Walker::start_el()1411 * @since 2.1.01412 *1413 * @param string $output Passed by reference. Used to append additional content.1414 * @param object $page Page data object.1415 * @param int $depth Depth of page. Used for padding.1416 * @param int $current_page Page ID.1417 * @param array $args1418 */1419 function start_el(&$output, $page, $depth, $args, $current_page) {1420 if ( $depth )1421 $indent = str_repeat("\t", $depth);1422 else1423 $indent = '';1424 1425 extract($args, EXTR_SKIP);1426 $css_class = array('page_item', 'page-item-'.$page->ID);1427 if ( !empty($current_page) ) {1428 $_current_page = get_page( $current_page );1429 if ( isset($_current_page->ancestors) && in_array($page->ID, (array) $_current_page->ancestors) )1430 $css_class[] = 'current_page_ancestor';1431 if ( $page->ID == $current_page )1432 $css_class[] = 'current_page_item';1433 elseif ( $_current_page && $page->ID == $_current_page->post_parent )1434 $css_class[] = 'current_page_parent';1435 } elseif ( $page->ID == get_option('page_for_posts') ) {1436 $css_class[] = 'current_page_parent';1437 }1438 1439 $css_class = implode(' ', apply_filters('page_css_class', $css_class, $page));1440 1441 $output .= $indent . '<li class="' . $css_class . '"><a href="' . get_permalink($page->ID) . '" title="' . esc_attr( wp_strip_all_tags( apply_filters( 'the_title', $page->post_title, $page->ID ) ) ) . '">' . $link_before . apply_filters( 'the_title', $page->post_title, $page->ID ) . $link_after . '</a>';1442 1443 if ( !empty($show_date) ) {1444 if ( 'modified' == $show_date )1445 $time = $page->post_modified;1446 else1447 $time = $page->post_date;1448 1449 $output .= " " . mysql2date($date_format, $time);1450 }1451 }1452 1453 /**1454 * @see Walker::end_el()1455 * @since 2.1.01456 *1457 * @param string $output Passed by reference. Used to append additional content.1458 * @param object $page Page data object. Not used.1459 * @param int $depth Depth of page. Not Used.1460 */1461 function end_el(&$output, $page, $depth) {1462 $output .= "</li>\n";1463 }1464 1465 }1466 1467 /**1468 * Create HTML dropdown list of pages.1469 *1470 * @package WordPress1471 * @since 2.1.01472 * @uses Walker1473 */1474 class Walker_PageDropdown extends Walker {1475 /**1476 * @see Walker::$tree_type1477 * @since 2.1.01478 * @var string1479 */1480 var $tree_type = 'page';1481 1482 /**1483 * @see Walker::$db_fields1484 * @since 2.1.01485 * @todo Decouple this1486 * @var array1487 */1488 var $db_fields = array ('parent' => 'post_parent', 'id' => 'ID');1489 1490 /**1491 * @see Walker::start_el()1492 * @since 2.1.01493 *1494 * @param string $output Passed by reference. Used to append additional content.1495 * @param object $page Page data object.1496 * @param int $depth Depth of page in reference to parent pages. Used for padding.1497 * @param array $args Uses 'selected' argument for selected page to set selected HTML attribute for option element.1498 */1499 function start_el(&$output, $page, $depth, $args) {1500 $pad = str_repeat(' ', $depth * 3);1501 1502 $output .= "\t<option class=\"level-$depth\" value=\"$page->ID\"";1503 if ( $page->ID == $args['selected'] )1504 $output .= ' selected="selected"';1505 $output .= '>';1506 $title = esc_html($page->post_title);1507 $output .= "$pad$title";1508 $output .= "</option>\n";1509 }1510 }1511 1512 /**1513 * Create HTML list of categories.1514 *1515 * @package WordPress1516 * @since 2.1.01517 * @uses Walker1518 */1519 class Walker_Category extends Walker {1520 /**1521 * @see Walker::$tree_type1522 * @since 2.1.01523 * @var string1524 */1525 var $tree_type = 'category';1526 1527 /**1528 * @see Walker::$db_fields1529 * @since 2.1.01530 * @todo Decouple this1531 * @var array1532 */1533 var $db_fields = array ('parent' => 'parent', 'id' => 'term_id');1534 1535 /**1536 * @see Walker::start_lvl()1537 * @since 2.1.01538 *1539 * @param string $output Passed by reference. Used to append additional content.1540 * @param int $depth Depth of category. Used for tab indentation.1541 * @param array $args Will only append content if style argument value is 'list'.1542 */1543 function start_lvl(&$output, $depth, $args) {1544 if ( 'list' != $args['style'] )1545 return;1546 1547 $indent = str_repeat("\t", $depth);1548 $output .= "$indent<ul class='children'>\n";1549 }1550 1551 /**1552 * @see Walker::end_lvl()1553 * @since 2.1.01554 *1555 * @param string $output Passed by reference. Used to append additional content.1556 * @param int $depth Depth of category. Used for tab indentation.1557 * @param array $args Will only append content if style argument value is 'list'.1558 */1559 function end_lvl(&$output, $depth, $args) {1560 if ( 'list' != $args['style'] )1561 return;1562 1563 $indent = str_repeat("\t", $depth);1564 $output .= "$indent</ul>\n";1565 }1566 1567 /**1568 * @see Walker::start_el()1569 * @since 2.1.01570 *1571 * @param string $output Passed by reference. Used to append additional content.1572 * @param object $category Category data object.1573 * @param int $depth Depth of category in reference to parents.1574 * @param array $args1575 */1576 function start_el(&$output, $category, $depth, $args) {1577 extract($args);1578 1579 $cat_name = esc_attr( $category->name );1580 $cat_name = apply_filters( 'list_cats', $cat_name, $category );1581 $link = '<a href="' . esc_attr( get_term_link($category) ) . '" ';1582 if ( $use_desc_for_title == 0 || empty($category->description) )1583 $link .= 'title="' . sprintf(__( 'View all posts filed under %s' ), $cat_name) . '"';1584 else1585 $link .= 'title="' . esc_attr( strip_tags( apply_filters( 'category_description', $category->description, $category ) ) ) . '"';1586 $link .= '>';1587 $link .= $cat_name . '</a>';1588 1589 if ( !empty($feed_image) || !empty($feed) ) {1590 $link .= ' ';1591 1592 if ( empty($feed_image) )1593 $link .= '(';1594 1595 $link .= '<a href="' . get_term_feed_link( $category->term_id, $category->taxonomy, $feed_type ) . '"';1596 1597 if ( empty($feed) ) {1598 $alt = ' alt="' . sprintf(__( 'Feed for all posts filed under %s' ), $cat_name ) . '"';1599 } else {1600 $title = ' title="' . $feed . '"';1601 $alt = ' alt="' . $feed . '"';1602 $name = $feed;1603 $link .= $title;1604 }1605 1606 $link .= '>';1607 1608 if ( empty($feed_image) )1609 $link .= $name;1610 else1611 $link .= "<img src='$feed_image'$alt$title" . ' />';1612 1613 $link .= '</a>';1614 1615 if ( empty($feed_image) )1616 $link .= ')';1617 }1618 1619 if ( !empty($show_count) )1620 $link .= ' (' . intval($category->count) . ')';1621 1622 if ( !empty($show_date) )1623 $link .= ' ' . gmdate('Y-m-d', $category->last_update_timestamp);1624 1625 if ( 'list' == $args['style'] ) {1626 $output .= "\t<li";1627 $class = 'cat-item cat-item-' . $category->term_id;1628 if ( !empty($current_category) ) {1629 $_current_category = get_term( $current_category, $category->taxonomy );1630 if ( $category->term_id == $current_category )1631 $class .= ' current-cat';1632 elseif ( $category->term_id == $_current_category->parent )1633 $class .= ' current-cat-parent';1634 }1635 $output .= ' class="' . $class . '"';1636 $output .= ">$link\n";1637 } else {1638 $output .= "\t$link<br />\n";1639 }1640 }1641 1642 /**1643 * @see Walker::end_el()1644 * @since 2.1.01645 *1646 * @param string $output Passed by reference. Used to append additional content.1647 * @param object $page Not used.1648 * @param int $depth Depth of category. Not used.1649 * @param array $args Only uses 'list' for whether should append to output.1650 */1651 function end_el(&$output, $page, $depth, $args) {1652 if ( 'list' != $args['style'] )1653 return;1654 1655 $output .= "</li>\n";1656 }1657 1658 }1659 1660 /**1661 * Create HTML dropdown list of Categories.1662 *1663 * @package WordPress1664 * @since 2.1.01665 * @uses Walker1666 */1667 class Walker_CategoryDropdown extends Walker {1668 /**1669 * @see Walker::$tree_type1670 * @since 2.1.01671 * @var string1672 */1673 var $tree_type = 'category';1674 1675 /**1676 * @see Walker::$db_fields1677 * @since 2.1.01678 * @todo Decouple this1679 * @var array1680 */1681 var $db_fields = array ('parent' => 'parent', 'id' => 'term_id');1682 1683 /**1684 * @see Walker::start_el()1685 * @since 2.1.01686 *1687 * @param string $output Passed by reference. Used to append additional content.1688 * @param object $category Category data object.1689 * @param int $depth Depth of category. Used for padding.1690 * @param array $args Uses 'selected', 'show_count', and 'show_last_update' keys, if they exist.1691 */1692 function start_el(&$output, $category, $depth, $args) {1693 $pad = str_repeat(' ', $depth * 3);1694 1695 $cat_name = apply_filters('list_cats', $category->name, $category);1696 $output .= "\t<option class=\"level-$depth\" value=\"".$category->term_id."\"";1697 if ( $category->term_id == $args['selected'] )1698 $output .= ' selected="selected"';1699 $output .= '>';1700 $output .= $pad.$cat_name;1701 if ( $args['show_count'] )1702 $output .= ' ('. $category->count .')';1703 if ( $args['show_last_update'] ) {1704 $format = 'Y-m-d';1705 $output .= ' ' . gmdate($format, $category->last_update_timestamp);1706 }1707 $output .= "</option>\n";1708 }1709 }1710 1711 /**1712 * Send XML response back to AJAX request.1713 *1714 * @package WordPress1715 * @since 2.1.01716 */1717 class WP_Ajax_Response {1718 /**1719 * Store XML responses to send.1720 *1721 * @since 2.1.01722 * @var array1723 * @access private1724 */1725 var $responses = array();1726 1727 /**1728 * PHP4 Constructor - Passes args to {@link WP_Ajax_Response::add()}.1729 *1730 * @since 2.1.01731 * @see WP_Ajax_Response::add()1732 *1733 * @param string|array $args Optional. Will be passed to add() method.1734 * @return WP_Ajax_Response1735 */1736 function WP_Ajax_Response( $args = '' ) {1737 if ( !empty($args) )1738 $this->add($args);1739 }1740 1741 /**1742 * Append to XML response based on given arguments.1743 *1744 * The arguments that can be passed in the $args parameter are below. It is1745 * also possible to pass a WP_Error object in either the 'id' or 'data'1746 * argument. The parameter isn't actually optional, content should be given1747 * in order to send the correct response.1748 *1749 * 'what' argument is a string that is the XMLRPC response type.1750 * 'action' argument is a boolean or string that acts like a nonce.1751 * 'id' argument can be WP_Error or an integer.1752 * 'old_id' argument is false by default or an integer of the previous ID.1753 * 'position' argument is an integer or a string with -1 = top, 1 = bottom,1754 * html ID = after, -html ID = before.1755 * 'data' argument is a string with the content or message.1756 * 'supplemental' argument is an array of strings that will be children of1757 * the supplemental element.1758 *1759 * @since 2.1.01760 *1761 * @param string|array $args Override defaults.1762 * @return string XML response.1763 */1764 function add( $args = '' ) {1765 $defaults = array(1766 'what' => 'object', 'action' => false,1767 'id' => '0', 'old_id' => false,1768 'position' => 1,1769 'data' => '', 'supplemental' => array()1770 );1771 1772 $r = wp_parse_args( $args, $defaults );1773 extract( $r, EXTR_SKIP );1774 $position = preg_replace( '/[^a-z0-9:_-]/i', '', $position );1775 1776 if ( is_wp_error($id) ) {1777 $data = $id;1778 $id = 0;1779 }1780 1781 $response = '';1782 if ( is_wp_error($data) ) {1783 foreach ( (array) $data->get_error_codes() as $code ) {1784 $response .= "<wp_error code='$code'><![CDATA[" . $data->get_error_message($code) . "]]></wp_error>";1785 if ( !$error_data = $data->get_error_data($code) )1786 continue;1787 $class = '';1788 if ( is_object($error_data) ) {1789 $class = ' class="' . get_class($error_data) . '"';1790 $error_data = get_object_vars($error_data);1791 }1792 1793 $response .= "<wp_error_data code='$code'$class>";1794 1795 if ( is_scalar($error_data) ) {1796 $response .= "<![CDATA[$error_data]]>";1797 } elseif ( is_array($error_data) ) {1798 foreach ( $error_data as $k => $v )1799 $response .= "<$k><![CDATA[$v]]></$k>";1800 }1801 1802 $response .= "</wp_error_data>";1803 }1804 } else {1805 $response = "<response_data><![CDATA[$data]]></response_data>";1806 }1807 1808 $s = '';1809 if ( is_array($supplemental) ) {1810 foreach ( $supplemental as $k => $v )1811 $s .= "<$k><![CDATA[$v]]></$k>";1812 $s = "<supplemental>$s</supplemental>";1813 }1814 1815 if ( false === $action )1816 $action = $_POST['action'];1817 1818 $x = '';1819 $x .= "<response action='{$action}_$id'>"; // The action attribute in the xml output is formatted like a nonce action1820 $x .= "<$what id='$id' " . ( false === $old_id ? '' : "old_id='$old_id' " ) . "position='$position'>";1821 $x .= $response;1822 $x .= $s;1823 $x .= "</$what>";1824 $x .= "</response>";1825 1826 $this->responses[] = $x;1827 return $x;1828 }1829 1830 /**1831 * Display XML formatted responses.1832 *1833 * Sets the content type header to text/xml.1834 *1835 * @since 2.1.01836 */1837 function send() {1838 header('Content-Type: text/xml');1839 echo "<?xml version='1.0' standalone='yes'?><wp_ajax>";1840 foreach ( (array) $this->responses as $response )1841 echo $response;1842 echo '</wp_ajax>';1843 die();1844 }1845 }1846 1847 /**1848 * Helper class to remove the need to use eval to replace $matches[] in query strings.1849 *1850 * @since 2.9.01851 */1852 class WP_MatchesMapRegex {1853 /**1854 * store for matches1855 *1856 * @access private1857 * @var array1858 */1859 var $_matches;1860 1861 /**1862 * store for mapping result1863 *1864 * @access public1865 * @var string1866 */1867 var $output;1868 1869 /**1870 * subject to perform mapping on (query string containing $matches[] references1871 *1872 * @access private1873 * @var string1874 */1875 var $_subject;1876 1877 /**1878 * regexp pattern to match $matches[] references1879 *1880 * @var string1881 */1882 var $_pattern = '(\$matches\[[1-9]+[0-9]*\])'; // magic number1883 1884 /**1885 * constructor1886 *1887 * @param string $subject subject if regex1888 * @param array $matches data to use in map1889 * @return self1890 */1891 function WP_MatchesMapRegex($subject, $matches) {1892 $this->_subject = $subject;1893 $this->_matches = $matches;1894 $this->output = $this->_map();1895 }1896 1897 /**1898 * Substitute substring matches in subject.1899 *1900 * static helper function to ease use1901 *1902 * @access public1903 * @param string $subject subject1904 * @param array $matches data used for subsitution1905 * @return string1906 */1907 function apply($subject, $matches) {1908 $oSelf =& new WP_MatchesMapRegex($subject, $matches);1909 return $oSelf->output;1910 }1911 1912 /**1913 * do the actual mapping1914 *1915 * @access private1916 * @return string1917 */1918 function _map() {1919 $callback = array(&$this, 'callback');1920 return preg_replace_callback($this->_pattern, $callback, $this->_subject);1921 }1922 1923 /**1924 * preg_replace_callback hook1925 *1926 * @access public1927 * @param array $matches preg_replace regexp matches1928 * @return string1929 */1930 function callback($matches) {1931 $index = intval(substr($matches[0], 9, -1));1932 return ( isset( $this->_matches[$index] ) ? urlencode($this->_matches[$index]) : '' );1933 }1934 1935 }1936 1937 249 ?> -
trunk/wp-includes/classes.php
r16018 r16051 526 526 527 527 /** 528 * WordPress Query class.529 *530 * Abstract class for handling advanced queries531 *532 * @package WordPress533 * @since 3.1.0534 */535 class WP_Object_Query {536 537 /**538 * Query vars, after parsing539 *540 * @since 3.1.0541 * @access public542 * @var array543 */544 var $query_vars;545 546 /**547 * Retrieve query variable.548 *549 * @since 3.1.0550 * @access public551 *552 * @param string $query_var Query variable key.553 * @return mixed554 */555 function get( $query_var ) {556 if ( isset( $this->query_vars[$query_var] ) )557 return $this->query_vars[$query_var];558 559 return '';560 }561 562 /**563 * Set query variable.564 *565 * @since 3.1.0566 * @access public567 *568 * @param string $query_var Query variable key.569 * @param mixed $value Query variable value.570 */571 function set( $query_var, $value ) {572 $this->query_vars[ $query_var ] = $value;573 }574 575 /*576 * Populates the $meta_query property577 *578 * @access protected579 * @since 3.1.0580 *581 * @param array $qv The query variables582 */583 function parse_meta_query( &$qv ) {584 $meta_query = array();585 586 // Simple query needs to be first for orderby=meta_value to work correctly587 foreach ( array( 'key', 'value', 'compare', 'type' ) as $key ) {588 if ( !empty( $qv[ "meta_$key" ] ) )589 $meta_query[0][ $key ] = $qv[ "meta_$key" ];590 }591 592 if ( !empty( $qv['meta_query'] ) && is_array( $qv['meta_query'] ) ) {593 $meta_query = array_merge( $meta_query, $qv['meta_query'] );594 }595 596 $qv['meta_query'] = $meta_query;597 }598 599 /*600 * Used internally to generate an SQL string for searching across multiple meta key = value pairs601 *602 * @access protected603 * @since 3.1.0604 *605 * @param array $meta_query List of metadata queries. A single query is an associative array:606 * - 'key' string The meta key607 * - 'value' string|array The meta value608 * - 'compare' (optional) string How to compare the key to the value.609 * Possible values: '=', '!=', '>', '>=', '<', '<=', 'LIKE', 'NOT LIKE', 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN'.610 * Default: '='611 * - 'type' string (optional) The type of the value.612 * Possible values: 'NUMERIC', 'BINARY', 'CHAR', 'DATE', 'DATETIME', 'DECIMAL', 'SIGNED', 'TIME', 'UNSIGNED'.613 * Default: 'CHAR'614 *615 * @param string $primary_table616 * @param string $primary_id_column617 * @param string $meta_table618 * @param string $meta_id_column619 * @return array( $join_sql, $where_sql )620 */621 function get_meta_sql( $meta_query, $primary_table, $primary_id_column, $meta_table, $meta_id_column ) {622 global $wpdb;623 624 $clauses = array();625 626 $join = '';627 $where = '';628 $i = 0;629 foreach ( $meta_query as $q ) {630 $meta_key = isset( $q['key'] ) ? trim( $q['key'] ) : '';631 $meta_value = isset( $q['value'] ) ? $q['value'] : '';632 $meta_compare = isset( $q['compare'] ) ? strtoupper( $q['compare'] ) : '=';633 $meta_type = isset( $q['type'] ) ? strtoupper( $q['type'] ) : 'CHAR';634 635 if ( ! in_array( $meta_compare, array( '=', '!=', '>', '>=', '<', '<=', 'LIKE', 'NOT LIKE', 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN' ) ) )636 $meta_compare = '=';637 638 if ( 'NUMERIC' == $meta_type )639 $meta_type = 'SIGNED';640 elseif ( ! in_array( $meta_type, array( 'BINARY', 'CHAR', 'DATE', 'DATETIME', 'DECIMAL', 'SIGNED', 'TIME', 'UNSIGNED' ) ) )641 $meta_type = 'CHAR';642 643 if ( empty( $meta_key ) && empty( $meta_value ) )644 continue;645 646 $alias = $i ? 'mt' . $i : $meta_table;647 648 $join .= "\nINNER JOIN $meta_table";649 $join .= $i ? " AS $alias" : '';650 $join .= " ON ($primary_table.$primary_id_column = $alias.$meta_id_column)";651 652 $i++;653 654 if ( !empty( $meta_key ) )655 $where .= $wpdb->prepare( " AND $alias.meta_key = %s", $meta_key );656 657 if ( in_array( $meta_compare, array( 'IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN' ) ) ) {658 if ( ! is_array( $meta_value ) )659 $meta_value = preg_split( '/[,\s]+/', $meta_value );660 } else {661 $meta_value = trim( $meta_value );662 }663 664 if ( empty( $meta_value ) )665 continue;666 667 if ( 'IN' == substr( $meta_compare, -2) ) {668 $meta_field_types = substr( str_repeat( ',%s', count( $meta_value ) ), 1 );669 $meta_compare_string = "($meta_field_types)";670 unset( $meta_field_types );671 } elseif ( 'BETWEEN' == substr( $meta_compare, -7) ) {672 $meta_value = array_slice( $meta_value, 0, 2 );673 $meta_compare_string = '%s AND %s';674 } elseif ( 'LIKE' == substr( $meta_compare, -4 ) ) {675 $meta_value = '%' . like_escape( $meta_value ) . '%';676 $meta_compare_string = '%s';677 } else {678 $meta_compare_string = '%s';679 }680 $where .= $wpdb->prepare( " AND CAST($alias.meta_value AS {$meta_type}) {$meta_compare} {$meta_compare_string}", $meta_value );681 unset($meta_compare_string);682 }683 684 return array( $join, $where );685 }686 687 /*688 * Used internally to generate an SQL string for searching across multiple taxonomies689 *690 * @access protected691 * @since 3.1.0692 *693 * @param array $tax_query List of taxonomy queries. A single taxonomy query is an associative array:694 * - 'taxonomy' string|array The taxonomy being queried695 * - 'terms' string|array The list of terms696 * - 'field' string (optional) Which term field is being used.697 * Possible values: 'term_id', 'slug' or 'name'698 * Default: 'slug'699 * - 'operator' string (optional)700 * Possible values: 'IN' and 'NOT IN'.701 * Default: 'IN'702 * - 'include_children' bool (optional) Whether to include child terms.703 * Default: true704 *705 * @param string $object_id_column706 * @return string707 */708 function get_tax_sql( $tax_query, $object_id_column ) {709 global $wpdb;710 711 $sql = array();712 foreach ( $tax_query as $query ) {713 if ( !isset( $query['include_children'] ) )714 $query['include_children'] = true;715 716 $query['do_query'] = false;717 718 $sql_single = get_objects_in_term( $query['terms'], $query['taxonomy'], $query );719 720 if ( empty( $sql_single ) )721 return ' AND 0 = 1';722 723 $sql[] = $sql_single;724 }725 726 if ( 1 == count( $sql ) ) {727 $ids = $wpdb->get_col( $sql[0] );728 } else {729 $r = "SELECT object_id FROM $wpdb->term_relationships WHERE 1=1";730 foreach ( $sql as $query )731 $r .= " AND object_id IN ($query)";732 733 $ids = $wpdb->get_col( $r );734 }735 736 if ( !empty( $ids ) )737 return " AND $object_id_column IN(" . implode( ', ', $ids ) . ")";738 else739 return ' AND 0 = 1';740 }741 742 /*743 * Used internally to generate an SQL string for searching across multiple columns744 *745 * @access protected746 * @since 3.1.0747 *748 * @param string $string749 * @param array $cols750 * @return string751 */752 function get_search_sql( $string, $cols ) {753 $string = esc_sql( $string );754 755 $searches = array();756 foreach ( $cols as $col )757 $searches[] = "$col LIKE '%$string%'";758 759 return ' AND (' . implode(' OR ', $searches) . ')';760 }761 }762 763 /**764 528 * WordPress Error class. 765 529 * -
trunk/wp-settings.php
r15811 r16051 103 103 require( ABSPATH . WPINC . '/formatting.php' ); 104 104 require( ABSPATH . WPINC . '/capabilities.php' ); 105 require( ABSPATH . WPINC . '/class.wp-object-query.php' ); 105 106 require( ABSPATH . WPINC . '/query.php' ); 106 107 require( ABSPATH . WPINC . '/theme.php' );
Note: See TracChangeset
for help on using the changeset viewer.