| [ Index ] |
PHP Cross Reference of WordPress (Trunk) |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Holds Most of the WordPress classes. 4 * 5 * Some of the other classes are contained in other files. For example, the 6 * WordPress cache is in cache.php and the WordPress roles API is in 7 * capabilities.php. The third party libraries are contained in their own 8 * separate files. 9 * 10 * @package WordPress 11 */ 12 13 /** 14 * WordPress environment setup class. 15 * 16 * @package WordPress 17 * @since 2.0.0 18 */ 19 class WP { 20 /** 21 * Public query variables. 22 * 23 * Long list of public query variables. 24 * 25 * @since 2.0.0 26 * @access public 27 * @var array 28 */ 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.0 37 * @var array 38 */ 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'); 40 41 /** 42 * Extra query variables set by the user. 43 * 44 * @since 2.1.0 45 * @var array 46 */ 47 var $extra_query_vars = array(); 48 49 /** 50 * Query variables for setting up the WordPress Query Loop. 51 * 52 * @since 2.0.0 53 * @var array 54 */ 55 var $query_vars; 56 57 /** 58 * String parsed to set the query variables. 59 * 60 * @since 2.0.0 61 * @var string 62 */ 63 var $query_string; 64 65 /** 66 * Permalink or requested URI. 67 * 68 * @since 2.0.0 69 * @var string 70 */ 71 var $request; 72 73 /** 74 * Rewrite rule the request matched. 75 * 76 * @since 2.0.0 77 * @var string 78 */ 79 var $matched_rule; 80 81 /** 82 * Rewrite query the request matched. 83 * 84 * @since 2.0.0 85 * @var string 86 */ 87 var $matched_query; 88 89 /** 90 * Whether already did the permalink. 91 * 92 * @since 2.0.0 93 * @var bool 94 */ 95 var $did_permalink = false; 96 97 /** 98 * Add name to list of public query variables. 99 * 100 * @since 2.1.0 101 * 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.0 113 * 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 many 125 * filters and actions that can be used to further manipulate the result. 126 * 127 * @since 2.0.0 128 * 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 $taxonomy_query_vars = array(); 136 $post_type_query_vars = array(); 137 138 if ( is_array($extra_query_vars) ) 139 $this->extra_query_vars = & $extra_query_vars; 140 else if (! empty($extra_query_vars)) 141 parse_str($extra_query_vars, $this->extra_query_vars); 142 143 // Process PATH_INFO, REQUEST_URI, and 404 for permalinks. 144 145 // Fetch the rewrite rules. 146 $rewrite = $wp_rewrite->wp_rewrite_rules(); 147 148 if ( ! empty($rewrite) ) { 149 // If we match a rewrite rule, this will be cleared. 150 $error = '404'; 151 $this->did_permalink = true; 152 153 if ( isset($_SERVER['PATH_INFO']) ) 154 $pathinfo = $_SERVER['PATH_INFO']; 155 else 156 $pathinfo = ''; 157 $pathinfo_array = explode('?', $pathinfo); 158 $pathinfo = str_replace("%", "%25", $pathinfo_array[0]); 159 $req_uri = $_SERVER['REQUEST_URI']; 160 $req_uri_array = explode('?', $req_uri); 161 $req_uri = $req_uri_array[0]; 162 $self = $_SERVER['PHP_SELF']; 163 $home_path = parse_url(home_url()); 164 if ( isset($home_path['path']) ) 165 $home_path = $home_path['path']; 166 else 167 $home_path = ''; 168 $home_path = trim($home_path, '/'); 169 170 // Trim path info from the end and the leading home path from the 171 // front. For path info requests, this leaves us with the requesting 172 // filename, if any. For 404 requests, this leaves us with the 173 // requested permalink. 174 $req_uri = str_replace($pathinfo, '', rawurldecode($req_uri)); 175 $req_uri = trim($req_uri, '/'); 176 $req_uri = preg_replace("|^$home_path|", '', $req_uri); 177 $req_uri = trim($req_uri, '/'); 178 $pathinfo = trim($pathinfo, '/'); 179 $pathinfo = preg_replace("|^$home_path|", '', $pathinfo); 180 $pathinfo = trim($pathinfo, '/'); 181 $self = trim($self, '/'); 182 $self = preg_replace("|^$home_path|", '', $self); 183 $self = trim($self, '/'); 184 185 // The requested permalink is in $pathinfo for path info requests and 186 // $req_uri for other requests. 187 if ( ! empty($pathinfo) && !preg_match('|^.*' . $wp_rewrite->index . '$|', $pathinfo) ) { 188 $request = $pathinfo; 189 } else { 190 // If the request uri is the index, blank it out so that we don't try to match it against a rule. 191 if ( $req_uri == $wp_rewrite->index ) 192 $req_uri = ''; 193 $request = $req_uri; 194 } 195 196 $this->request = $request; 197 198 // Look for matches. 199 $request_match = $request; 200 foreach ( (array) $rewrite as $match => $query) { 201 // Don't try to match against AtomPub calls 202 if ( $req_uri == 'wp-app.php' ) 203 break; 204 205 // If the requesting file is the anchor of the match, prepend it 206 // to the path info. 207 if ( (! empty($req_uri)) && (strpos($match, $req_uri) === 0) && ($req_uri != $request) ) 208 $request_match = $req_uri . '/' . $request; 209 210 if ( preg_match("#^$match#", $request_match, $matches) || 211 preg_match("#^$match#", urldecode($request_match), $matches) ) { 212 // Got a match. 213 $this->matched_rule = $match; 214 215 // Trim the query of everything up to the '?'. 216 $query = preg_replace("!^.+\?!", '', $query); 217 218 // Substitute the substring matches into the query. 219 $query = addslashes(WP_MatchesMapRegex::apply($query, $matches)); 220 221 $this->matched_query = $query; 222 223 // Parse the query. 224 parse_str($query, $perma_query_vars); 225 226 // If we're processing a 404 request, clear the error var 227 // since we found something. 228 if ( isset($_GET['error']) ) 229 unset($_GET['error']); 230 231 if ( isset($error) ) 232 unset($error); 233 234 break; 235 } 236 } 237 238 // If req_uri is empty or if it is a request for ourself, unset error. 239 if ( empty($request) || $req_uri == $self || strpos($_SERVER['PHP_SELF'], 'wp-admin/') !== false ) { 240 if ( isset($_GET['error']) ) 241 unset($_GET['error']); 242 243 if ( isset($error) ) 244 unset($error); 245 246 if ( isset($perma_query_vars) && strpos($_SERVER['PHP_SELF'], 'wp-admin/') !== false ) 247 unset($perma_query_vars); 248 249 $this->did_permalink = false; 250 } 251 } 252 253 $this->public_query_vars = apply_filters('query_vars', $this->public_query_vars); 254 255 foreach ( $GLOBALS['wp_taxonomies'] as $taxonomy => $t ) 256 if ( $t->query_var ) 257 $taxonomy_query_vars[$t->query_var] = $taxonomy; 258 259 foreach ( $GLOBALS['wp_post_types'] as $post_type => $t ) 260 if ( $t->query_var ) 261 $post_type_query_vars[$t->query_var] = $post_type; 262 263 for ( $i = 0; $i < count($this->public_query_vars); $i += 1 ) { 264 $wpvar = $this->public_query_vars[$i]; 265 if ( isset($this->extra_query_vars[$wpvar]) ) 266 $this->query_vars[$wpvar] = $this->extra_query_vars[$wpvar]; 267 elseif ( isset($GLOBALS[$wpvar]) ) 268 $this->query_vars[$wpvar] = $GLOBALS[$wpvar]; 269 elseif ( !empty($_POST[$wpvar]) ) 270 $this->query_vars[$wpvar] = $_POST[$wpvar]; 271 elseif ( !empty($_GET[$wpvar]) ) 272 $this->query_vars[$wpvar] = $_GET[$wpvar]; 273 elseif ( !empty($perma_query_vars[$wpvar]) ) 274 $this->query_vars[$wpvar] = $perma_query_vars[$wpvar]; 275 276 if ( !empty( $this->query_vars[$wpvar] ) ) { 277 $this->query_vars[$wpvar] = (string) $this->query_vars[$wpvar]; 278 if ( isset( $taxonomy_query_vars[$wpvar] ) ) { 279 $this->query_vars['taxonomy'] = $taxonomy_query_vars[$wpvar]; 280 $this->query_vars['term'] = $this->query_vars[$wpvar]; 281 } elseif ( isset($post_type_query_vars[$wpvar] ) ) { 282 $this->query_vars['post_type'] = $post_type_query_vars[$wpvar]; 283 $this->query_vars['name'] = $this->query_vars[$wpvar]; 284 } 285 } 286 } 287 288 // Limit publicly queried post_types to those that are publicly_queryable 289 if ( isset( $this->query_vars['post_type']) ) { 290 $queryable_post_types = get_post_types( array('publicly_queryable' => true) ); 291 if ( ! in_array( $this->query_vars['post_type'], $queryable_post_types ) ) 292 unset( $this->query_vars['post_type'] ); 293 } 294 295 foreach ( (array) $this->private_query_vars as $var) { 296 if ( isset($this->extra_query_vars[$var]) ) 297 $this->query_vars[$var] = $this->extra_query_vars[$var]; 298 elseif ( isset($GLOBALS[$var]) && '' != $GLOBALS[$var] ) 299 $this->query_vars[$var] = $GLOBALS[$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 showing 314 * a feed, it will also send last-modified, etag, and 304 status if needed. 315 * 316 * @since 2.0.0 317 */ 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 changed 334 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 else 347 $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 GET 353 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 timestamp 359 $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 should 389 * use the 'request' filter instead. 390 * 391 * @since 2.0.0 392 */ 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 should 415 * be taken when naming global variables that might interfere with the 416 * 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.0 423 */ 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'] = $wp_query->post; 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.0 446 */ 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_vars 455 * @since 2.0.0 456 */ 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 match 467 * any object (e.g. an existing-but-empty category, tag, author) and a 404 was not already 468 * issued, and if the request was not a search or the homepage. 469 * 470 * Otherwise, issue a 200. 471 * 472 * @since 2.0.0 473 */ 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. It 496 * allows for accessing the properties and methods to further manipulate the 497 * object. 498 * 499 * @since 2.0.0 500 * 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.0 519 * 520 * @return WP 521 */ 522 function WP() { 523 // Empty. 524 } 525 } 526 527 /** 528 * WordPress Error class. 529 * 530 * Container for checking for WordPress errors and error messages. Return 531 * WP_Error and use {@link is_wp_error()} to check if this class is returned. 532 * Many core WordPress functions pass this class in the event of an error and 533 * if not handled properly will result in code errors. 534 * 535 * @package WordPress 536 * @since 2.1.0 537 */ 538 class WP_Error { 539 /** 540 * Stores the list of errors. 541 * 542 * @since 2.1.0 543 * @var array 544 * @access private 545 */ 546 var $errors = array(); 547 548 /** 549 * Stores the list of data for error codes. 550 * 551 * @since 2.1.0 552 * @var array 553 * @access private 554 */ 555 var $error_data = array(); 556 557 /** 558 * PHP4 Constructor - Sets up error message. 559 * 560 * If code parameter is empty then nothing will be done. It is possible to 561 * add multiple messages to the same code, but with other methods in the 562 * class. 563 * 564 * All parameters are optional, but if the code parameter is set, then the 565 * data parameter is optional. 566 * 567 * @since 2.1.0 568 * 569 * @param string|int $code Error code 570 * @param string $message Error message 571 * @param mixed $data Optional. Error data. 572 * @return WP_Error 573 */ 574 function WP_Error($code = '', $message = '', $data = '') { 575 if ( empty($code) ) 576 return; 577 578 $this->errors[$code][] = $message; 579 580 if ( ! empty($data) ) 581 $this->error_data[$code] = $data; 582 } 583 584 /** 585 * Retrieve all error codes. 586 * 587 * @since 2.1.0 588 * @access public 589 * 590 * @return array List of error codes, if avaiable. 591 */ 592 function get_error_codes() { 593 if ( empty($this->errors) ) 594 return array(); 595 596 return array_keys($this->errors); 597 } 598 599 /** 600 * Retrieve first error code available. 601 * 602 * @since 2.1.0 603 * @access public 604 * 605 * @return string|int Empty string, if no error codes. 606 */ 607 function get_error_code() { 608 $codes = $this->get_error_codes(); 609 610 if ( empty($codes) ) 611 return ''; 612 613 return $codes[0]; 614 } 615 616 /** 617 * Retrieve all error messages or error messages matching code. 618 * 619 * @since 2.1.0 620 * 621 * @param string|int $code Optional. Retrieve messages matching code, if exists. 622 * @return array Error strings on success, or empty array on failure (if using codee parameter). 623 */ 624 function get_error_messages($code = '') { 625 // Return all messages if no code specified. 626 if ( empty($code) ) { 627 $all_messages = array(); 628 foreach ( (array) $this->errors as $code => $messages ) 629 $all_messages = array_merge($all_messages, $messages); 630 631 return $all_messages; 632 } 633 634 if ( isset($this->errors[$code]) ) 635 return $this->errors[$code]; 636 else 637 return array(); 638 } 639 640 /** 641 * Get single error message. 642 * 643 * This will get the first message available for the code. If no code is 644 * given then the first code available will be used. 645 * 646 * @since 2.1.0 647 * 648 * @param string|int $code Optional. Error code to retrieve message. 649 * @return string 650 */ 651 function get_error_message($code = '') { 652 if ( empty($code) ) 653 $code = $this->get_error_code(); 654 $messages = $this->get_error_messages($code); 655 if ( empty($messages) ) 656 return ''; 657 return $messages[0]; 658 } 659 660 /** 661 * Retrieve error data for error code. 662 * 663 * @since 2.1.0 664 * 665 * @param string|int $code Optional. Error code. 666 * @return mixed Null, if no errors. 667 */ 668 function get_error_data($code = '') { 669 if ( empty($code) ) 670 $code = $this->get_error_code(); 671 672 if ( isset($this->error_data[$code]) ) 673 return $this->error_data[$code]; 674 return null; 675 } 676 677 /** 678 * Append more error messages to list of error messages. 679 * 680 * @since 2.1.0 681 * @access public 682 * 683 * @param string|int $code Error code. 684 * @param string $message Error message. 685 * @param mixed $data Optional. Error data. 686 */ 687 function add($code, $message, $data = '') { 688 $this->errors[$code][] = $message; 689 if ( ! empty($data) ) 690 $this->error_data[$code] = $data; 691 } 692 693 /** 694 * Add data for error code. 695 * 696 * The error code can only contain one error data. 697 * 698 * @since 2.1.0 699 * 700 * @param mixed $data Error data. 701 * @param string|int $code Error code. 702 */ 703 function add_data($data, $code = '') { 704 if ( empty($code) ) 705 $code = $this->get_error_code(); 706 707 $this->error_data[$code] = $data; 708 } 709 } 710 711 /** 712 * Check whether variable is a WordPress Error. 713 * 714 * Looks at the object and if a WP_Error class. Does not check to see if the 715 * parent is also WP_Error, so can't inherit WP_Error and still use this 716 * function. 717 * 718 * @since 2.1.0 719 * 720 * @param mixed $thing Check if unknown variable is WordPress Error object. 721 * @return bool True, if WP_Error. False, if not WP_Error. 722 */ 723 function is_wp_error($thing) { 724 if ( is_object($thing) && is_a($thing, 'WP_Error') ) 725 return true; 726 return false; 727 } 728 729 /** 730 * A class for displaying various tree-like structures. 731 * 732 * Extend the Walker class to use it, see examples at the below. Child classes 733 * do not need to implement all of the abstract methods in the class. The child 734 * only needs to implement the methods that are needed. Also, the methods are 735 * not strictly abstract in that the parameter definition needs to be followed. 736 * The child classes can have additional parameters. 737 * 738 * @package WordPress 739 * @since 2.1.0 740 * @abstract 741 */ 742 class Walker { 743 /** 744 * What the class handles. 745 * 746 * @since 2.1.0 747 * @var string 748 * @access public 749 */ 750 var $tree_type; 751 752 /** 753 * DB fields to use. 754 * 755 * @since 2.1.0 756 * @var array 757 * @access protected 758 */ 759 var $db_fields; 760 761 /** 762 * Max number of pages walked by the paged walker 763 * 764 * @since 2.7.0 765 * @var int 766 * @access protected 767 */ 768 var $max_pages = 1; 769 770 /** 771 * Starts the list before the elements are added. 772 * 773 * Additional parameters are used in child classes. The args parameter holds 774 * additional values that may be used with the child class methods. This 775 * method is called at the start of the output list. 776 * 777 * @since 2.1.0 778 * @abstract 779 * 780 * @param string $output Passed by reference. Used to append additional content. 781 */ 782 function start_lvl(&$output) {} 783 784 /** 785 * Ends the list of after the elements are added. 786 * 787 * Additional parameters are used in child classes. The args parameter holds 788 * additional values that may be used with the child class methods. This 789 * method finishes the list at the end of output of the elements. 790 * 791 * @since 2.1.0 792 * @abstract 793 * 794 * @param string $output Passed by reference. Used to append additional content. 795 */ 796 function end_lvl(&$output) {} 797 798 /** 799 * Start the element output. 800 * 801 * Additional parameters are used in child classes. The args parameter holds 802 * additional values that may be used with the child class methods. Includes 803 * the element output also. 804 * 805 * @since 2.1.0 806 * @abstract 807 * 808 * @param string $output Passed by reference. Used to append additional content. 809 */ 810 function start_el(&$output) {} 811 812 /** 813 * Ends the element output, if needed. 814 * 815 * Additional parameters are used in child classes. The args parameter holds 816 * additional values that may be used with the child class methods. 817 * 818 * @since 2.1.0 819 * @abstract 820 * 821 * @param string $output Passed by reference. Used to append additional content. 822 */ 823 function end_el(&$output) {} 824 825 /** 826 * Traverse elements to create list from elements. 827 * 828 * Display one element if the element doesn't have any children otherwise, 829 * display the element and its children. Will only traverse up to the max 830 * depth and no ignore elements under that depth. It is possible to set the 831 * max depth to include all depths, see walk() method. 832 * 833 * This method shouldn't be called directly, use the walk() method instead. 834 * 835 * @since 2.5.0 836 * 837 * @param object $element Data object 838 * @param array $children_elements List of elements to continue traversing. 839 * @param int $max_depth Max depth to traverse. 840 * @param int $depth Depth of current element. 841 * @param array $args 842 * @param string $output Passed by reference. Used to append additional content. 843 * @return null Null on failure with no changes to parameters. 844 */ 845 function display_element( $element, &$children_elements, $max_depth, $depth=0, $args, &$output ) { 846 847 if ( !$element ) 848 return; 849 850 $id_field = $this->db_fields['id']; 851 852 //display this element 853 if ( is_array( $args[0] ) ) 854 $args[0]['has_children'] = ! empty( $children_elements[$element->$id_field] ); 855 $cb_args = array_merge( array(&$output, $element, $depth), $args); 856 call_user_func_array(array(&$this, 'start_el'), $cb_args); 857 858 $id = $element->$id_field; 859 860 // descend only when the depth is right and there are childrens for this element 861 if ( ($max_depth == 0 || $max_depth > $depth+1 ) && isset( $children_elements[$id]) ) { 862 863 foreach( $children_elements[ $id ] as $child ){ 864 865 if ( !isset($newlevel) ) { 866 $newlevel = true; 867 //start the child delimiter 868 $cb_args = array_merge( array(&$output, $depth), $args); 869 call_user_func_array(array(&$this, 'start_lvl'), $cb_args); 870 } 871 $this->display_element( $child, $children_elements, $max_depth, $depth + 1, $args, $output ); 872 } 873 unset( $children_elements[ $id ] ); 874 } 875 876 if ( isset($newlevel) && $newlevel ){ 877 //end the child delimiter 878 $cb_args = array_merge( array(&$output, $depth), $args); 879 call_user_func_array(array(&$this, 'end_lvl'), $cb_args); 880 } 881 882 //end this element 883 $cb_args = array_merge( array(&$output, $element, $depth), $args); 884 call_user_func_array(array(&$this, 'end_el'), $cb_args); 885 } 886 887 /** 888 * Display array of elements hierarchically. 889 * 890 * It is a generic function which does not assume any existing order of 891 * elements. max_depth = -1 means flatly display every element. max_depth = 892 * 0 means display all levels. max_depth > 0 specifies the number of 893 * display levels. 894 * 895 * @since 2.1.0 896 * 897 * @param array $elements 898 * @param int $max_depth 899 * @return string 900 */ 901 function walk( $elements, $max_depth) { 902 903 $args = array_slice(func_get_args(), 2); 904 $output = ''; 905 906 if ($max_depth < -1) //invalid parameter 907 return $output; 908 909 if (empty($elements)) //nothing to walk 910 return $output; 911 912 $id_field = $this->db_fields['id']; 913 $parent_field = $this->db_fields['parent']; 914 915 // flat display 916 if ( -1 == $max_depth ) { 917 $empty_array = array(); 918 foreach ( $elements as $e ) 919 $this->display_element( $e, $empty_array, 1, 0, $args, $output ); 920 return $output; 921 } 922 923 /* 924 * need to display in hierarchical order 925 * separate elements into two buckets: top level and children elements 926 * children_elements is two dimensional array, eg. 927 * children_elements[10][] contains all sub-elements whose parent is 10. 928 */ 929 $top_level_elements = array(); 930 $children_elements = array(); 931 foreach ( $elements as $e) { 932 if ( 0 == $e->$parent_field ) 933 $top_level_elements[] = $e; 934 else 935 $children_elements[ $e->$parent_field ][] = $e; 936 } 937 938 /* 939 * when none of the elements is top level 940 * assume the first one must be root of the sub elements 941 */ 942 if ( empty($top_level_elements) ) { 943 944 $first = array_slice( $elements, 0, 1 ); 945 $root = $first[0]; 946 947 $top_level_elements = array(); 948 $children_elements = array(); 949 foreach ( $elements as $e) { 950 if ( $root->$parent_field == $e->$parent_field ) 951 $top_level_elements[] = $e; 952 else 953 $children_elements[ $e->$parent_field ][] = $e; 954 } 955 } 956 957 foreach ( $top_level_elements as $e ) 958 $this->display_element( $e, $children_elements, $max_depth, 0, $args, $output ); 959 960 /* 961 * if we are displaying all levels, and remaining children_elements is not empty, 962 * then we got orphans, which should be displayed regardless 963 */ 964 if ( ( $max_depth == 0 ) && count( $children_elements ) > 0 ) { 965 $empty_array = array(); 966 foreach ( $children_elements as $orphans ) 967 foreach( $orphans as $op ) 968 $this->display_element( $op, $empty_array, 1, 0, $args, $output ); 969 } 970 971 return $output; 972 } 973 974 /** 975 * paged_walk() - produce a page of nested elements 976 * 977 * Given an array of hierarchical elements, the maximum depth, a specific page number, 978 * and number of elements per page, this function first determines all top level root elements 979 * belonging to that page, then lists them and all of their children in hierarchical order. 980 * 981 * @package WordPress 982 * @since 2.7 983 * @param int $max_depth = 0 means display all levels; $max_depth > 0 specifies the number of display levels. 984 * @param int $page_num the specific page number, beginning with 1. 985 * @return XHTML of the specified page of elements 986 */ 987 function paged_walk( $elements, $max_depth, $page_num, $per_page ) { 988 989 /* sanity check */ 990 if ( empty($elements) || $max_depth < -1 ) 991 return ''; 992 993 $args = array_slice( func_get_args(), 4 ); 994 $output = ''; 995 996 $id_field = $this->db_fields['id']; 997 $parent_field = $this->db_fields['parent']; 998 999 $count = -1; 1000 if ( -1 == $max_depth ) 1001 $total_top = count( $elements ); 1002 if ( $page_num < 1 || $per_page < 0 ) { 1003 // No paging 1004 $paging = false; 1005 $start = 0; 1006 if ( -1 == $max_depth ) 1007 $end = $total_top; 1008 $this->max_pages = 1; 1009 } else { 1010 $paging = true; 1011 $start = ( (int)$page_num - 1 ) * (int)$per_page; 1012 $end = $start + $per_page; 1013 if ( -1 == $max_depth ) 1014 $this->max_pages = ceil($total_top / $per_page); 1015 } 1016 1017 // flat display 1018 if ( -1 == $max_depth ) { 1019 if ( !empty($args[0]['reverse_top_level']) ) { 1020 $elements = array_reverse( $elements ); 1021 $oldstart = $start; 1022 $start = $total_top - $end; 1023 $end = $total_top - $oldstart; 1024 } 1025 1026 $empty_array = array(); 1027 foreach ( $elements as $e ) { 1028 $count++; 1029 if ( $count < $start ) 1030 continue; 1031 if ( $count >= $end ) 1032 break; 1033 $this->display_element( $e, $empty_array, 1, 0, $args, $output ); 1034 } 1035 return $output; 1036 } 1037 1038 /* 1039 * separate elements into two buckets: top level and children elements 1040 * children_elements is two dimensional array, eg. 1041 * children_elements[10][] contains all sub-elements whose parent is 10. 1042 */ 1043 $top_level_elements = array(); 1044 $children_elements = array(); 1045 foreach ( $elements as $e) { 1046 if ( 0 == $e->$parent_field ) 1047 $top_level_elements[] = $e; 1048 else 1049 $children_elements[ $e->$parent_field ][] = $e; 1050 } 1051 1052 $total_top = count( $top_level_elements ); 1053 if ( $paging ) 1054 $this->max_pages = ceil($total_top / $per_page); 1055 else 1056 $end = $total_top; 1057 1058 if ( !empty($args[0]['reverse_top_level']) ) { 1059 $top_level_elements = array_reverse( $top_level_elements ); 1060 $oldstart = $start; 1061 $start = $total_top - $end; 1062 $end = $total_top - $oldstart; 1063 } 1064 if ( !empty($args[0]['reverse_children']) ) { 1065 foreach ( $children_elements as $parent => $children ) 1066 $children_elements[$parent] = array_reverse( $children ); 1067 } 1068 1069 foreach ( $top_level_elements as $e ) { 1070 $count++; 1071 1072 //for the last page, need to unset earlier children in order to keep track of orphans 1073 if ( $end >= $total_top && $count < $start ) 1074 $this->unset_children( $e, $children_elements ); 1075 1076 if ( $count < $start ) 1077 continue; 1078 1079 if ( $count >= $end ) 1080 break; 1081 1082 $this->display_element( $e, $children_elements, $max_depth, 0, $args, $output ); 1083 } 1084 1085 if ( $end >= $total_top && count( $children_elements ) > 0 ) { 1086 $empty_array = array(); 1087 foreach ( $children_elements as $orphans ) 1088 foreach( $orphans as $op ) 1089 $this->display_element( $op, $empty_array, 1, 0, $args, $output ); 1090 } 1091 1092 return $output; 1093 } 1094 1095 function get_number_of_root_elements( $elements ){ 1096 1097 $num = 0; 1098 $parent_field = $this->db_fields['parent']; 1099 1100 foreach ( $elements as $e) { 1101 if ( 0 == $e->$parent_field ) 1102 $num++; 1103 } 1104 return $num; 1105 } 1106 1107 // unset all the children for a given top level element 1108 function unset_children( $e, &$children_elements ){ 1109 1110 if ( !$e || !$children_elements ) 1111 return; 1112 1113 $id_field = $this->db_fields['id']; 1114 $id = $e->$id_field; 1115 1116 if ( !empty($children_elements[$id]) && is_array($children_elements[$id]) ) 1117 foreach ( (array) $children_elements[$id] as $child ) 1118 $this->unset_children( $child, $children_elements ); 1119 1120 if ( isset($children_elements[$id]) ) 1121 unset( $children_elements[$id] ); 1122 1123 } 1124 } 1125 1126 /** 1127 * Create HTML list of pages. 1128 * 1129 * @package WordPress 1130 * @since 2.1.0 1131 * @uses Walker 1132 */ 1133 class Walker_Page extends Walker { 1134 /** 1135 * @see Walker::$tree_type 1136 * @since 2.1.0 1137 * @var string 1138 */ 1139 var $tree_type = 'page'; 1140 1141 /** 1142 * @see Walker::$db_fields 1143 * @since 2.1.0 1144 * @todo Decouple this. 1145 * @var array 1146 */ 1147 var $db_fields = array ('parent' => 'post_parent', 'id' => 'ID'); 1148 1149 /** 1150 * @see Walker::start_lvl() 1151 * @since 2.1.0 1152 * 1153 * @param string $output Passed by reference. Used to append additional content. 1154 * @param int $depth Depth of page. Used for padding. 1155 */ 1156 function start_lvl(&$output, $depth) { 1157 $indent = str_repeat("\t", $depth); 1158 $output .= "\n$indent<ul class='children'>\n"; 1159 } 1160 1161 /** 1162 * @see Walker::end_lvl() 1163 * @since 2.1.0 1164 * 1165 * @param string $output Passed by reference. Used to append additional content. 1166 * @param int $depth Depth of page. Used for padding. 1167 */ 1168 function end_lvl(&$output, $depth) { 1169 $indent = str_repeat("\t", $depth); 1170 $output .= "$indent</ul>\n"; 1171 } 1172 1173 /** 1174 * @see Walker::start_el() 1175 * @since 2.1.0 1176 * 1177 * @param string $output Passed by reference. Used to append additional content. 1178 * @param object $page Page data object. 1179 * @param int $depth Depth of page. Used for padding. 1180 * @param int $current_page Page ID. 1181 * @param array $args 1182 */ 1183 function start_el(&$output, $page, $depth, $args, $current_page) { 1184 if ( $depth ) 1185 $indent = str_repeat("\t", $depth); 1186 else 1187 $indent = ''; 1188 1189 extract($args, EXTR_SKIP); 1190 $css_class = array('page_item', 'page-item-'.$page->ID); 1191 if ( !empty($current_page) ) { 1192 $_current_page = get_page( $current_page ); 1193 if ( isset($_current_page->ancestors) && in_array($page->ID, (array) $_current_page->ancestors) ) 1194 $css_class[] = 'current_page_ancestor'; 1195 if ( $page->ID == $current_page ) 1196 $css_class[] = 'current_page_item'; 1197 elseif ( $_current_page && $page->ID == $_current_page->post_parent ) 1198 $css_class[] = 'current_page_parent'; 1199 } elseif ( $page->ID == get_option('page_for_posts') ) { 1200 $css_class[] = 'current_page_parent'; 1201 } 1202 1203 $css_class = implode(' ', apply_filters('page_css_class', $css_class, $page)); 1204 1205 $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>'; 1206 1207 if ( !empty($show_date) ) { 1208 if ( 'modified' == $show_date ) 1209 $time = $page->post_modified; 1210 else 1211 $time = $page->post_date; 1212 1213 $output .= " " . mysql2date($date_format, $time); 1214 } 1215 } 1216 1217 /** 1218 * @see Walker::end_el() 1219 * @since 2.1.0 1220 * 1221 * @param string $output Passed by reference. Used to append additional content. 1222 * @param object $page Page data object. Not used. 1223 * @param int $depth Depth of page. Not Used. 1224 */ 1225 function end_el(&$output, $page, $depth) { 1226 $output .= "</li>\n"; 1227 } 1228 1229 } 1230 1231 /** 1232 * Create HTML dropdown list of pages. 1233 * 1234 * @package WordPress 1235 * @since 2.1.0 1236 * @uses Walker 1237 */ 1238 class Walker_PageDropdown extends Walker { 1239 /** 1240 * @see Walker::$tree_type 1241 * @since 2.1.0 1242 * @var string 1243 */ 1244 var $tree_type = 'page'; 1245 1246 /** 1247 * @see Walker::$db_fields 1248 * @since 2.1.0 1249 * @todo Decouple this 1250 * @var array 1251 */ 1252 var $db_fields = array ('parent' => 'post_parent', 'id' => 'ID'); 1253 1254 /** 1255 * @see Walker::start_el() 1256 * @since 2.1.0 1257 * 1258 * @param string $output Passed by reference. Used to append additional content. 1259 * @param object $page Page data object. 1260 * @param int $depth Depth of page in reference to parent pages. Used for padding. 1261 * @param array $args Uses 'selected' argument for selected page to set selected HTML attribute for option element. 1262 */ 1263 function start_el(&$output, $page, $depth, $args) { 1264 $pad = str_repeat(' ', $depth * 3); 1265 1266 $output .= "\t<option class=\"level-$depth\" value=\"$page->ID\""; 1267 if ( $page->ID == $args['selected'] ) 1268 $output .= ' selected="selected"'; 1269 $output .= '>'; 1270 $title = esc_html($page->post_title); 1271 $output .= "$pad$title"; 1272 $output .= "</option>\n"; 1273 } 1274 } 1275 1276 /** 1277 * Create HTML list of categories. 1278 * 1279 * @package WordPress 1280 * @since 2.1.0 1281 * @uses Walker 1282 */ 1283 class Walker_Category extends Walker { 1284 /** 1285 * @see Walker::$tree_type 1286 * @since 2.1.0 1287 * @var string 1288 */ 1289 var $tree_type = 'category'; 1290 1291 /** 1292 * @see Walker::$db_fields 1293 * @since 2.1.0 1294 * @todo Decouple this 1295 * @var array 1296 */ 1297 var $db_fields = array ('parent' => 'parent', 'id' => 'term_id'); 1298 1299 /** 1300 * @see Walker::start_lvl() 1301 * @since 2.1.0 1302 * 1303 * @param string $output Passed by reference. Used to append additional content. 1304 * @param int $depth Depth of category. Used for tab indentation. 1305 * @param array $args Will only append content if style argument value is 'list'. 1306 */ 1307 function start_lvl(&$output, $depth, $args) { 1308 if ( 'list' != $args['style'] ) 1309 return; 1310 1311 $indent = str_repeat("\t", $depth); 1312 $output .= "$indent<ul class='children'>\n"; 1313 } 1314 1315 /** 1316 * @see Walker::end_lvl() 1317 * @since 2.1.0 1318 * 1319 * @param string $output Passed by reference. Used to append additional content. 1320 * @param int $depth Depth of category. Used for tab indentation. 1321 * @param array $args Will only append content if style argument value is 'list'. 1322 */ 1323 function end_lvl(&$output, $depth, $args) { 1324 if ( 'list' != $args['style'] ) 1325 return; 1326 1327 $indent = str_repeat("\t", $depth); 1328 $output .= "$indent</ul>\n"; 1329 } 1330 1331 /** 1332 * @see Walker::start_el() 1333 * @since 2.1.0 1334 * 1335 * @param string $output Passed by reference. Used to append additional content. 1336 * @param object $category Category data object. 1337 * @param int $depth Depth of category in reference to parents. 1338 * @param array $args 1339 */ 1340 function start_el(&$output, $category, $depth, $args) { 1341 extract($args); 1342 1343 $cat_name = esc_attr( $category->name ); 1344 $cat_name = apply_filters( 'list_cats', $cat_name, $category ); 1345 $link = '<a href="' . esc_attr( get_term_link($category) ) . '" '; 1346 if ( $use_desc_for_title == 0 || empty($category->description) ) 1347 $link .= 'title="' . sprintf(__( 'View all posts filed under %s' ), $cat_name) . '"'; 1348 else 1349 $link .= 'title="' . esc_attr( strip_tags( apply_filters( 'category_description', $category->description, $category ) ) ) . '"'; 1350 $link .= '>'; 1351 $link .= $cat_name . '</a>'; 1352 1353 if ( (! empty($feed_image)) || (! empty($feed)) ) { 1354 $link .= ' '; 1355 1356 if ( empty($feed_image) ) 1357 $link .= '('; 1358 1359 $link .= '<a href="' . get_term_feed_link( $category->term_id, $category->taxonomy, $feed_type ) . '"'; 1360 1361 if ( empty($feed) ) 1362 $alt = ' alt="' . sprintf(__( 'Feed for all posts filed under %s' ), $cat_name ) . '"'; 1363 else { 1364 $title = ' title="' . $feed . '"'; 1365 $alt = ' alt="' . $feed . '"'; 1366 $name = $feed; 1367 $link .= $title; 1368 } 1369 1370 $link .= '>'; 1371 1372 if ( empty($feed_image) ) 1373 $link .= $name; 1374 else 1375 $link .= "<img src='$feed_image'$alt$title" . ' />'; 1376 $link .= '</a>'; 1377 if ( empty($feed_image) ) 1378 $link .= ')'; 1379 } 1380 1381 if ( isset($show_count) && $show_count ) 1382 $link .= ' (' . intval($category->count) . ')'; 1383 1384 if ( isset($show_date) && $show_date ) { 1385 $link .= ' ' . gmdate('Y-m-d', $category->last_update_timestamp); 1386 } 1387 1388 if ( isset($current_category) && $current_category ) 1389 $_current_category = get_category( $current_category ); 1390 1391 if ( 'list' == $args['style'] ) { 1392 $output .= "\t<li"; 1393 $class = 'cat-item cat-item-'.$category->term_id; 1394 if ( isset($current_category) && $current_category && ($category->term_id == $current_category) ) 1395 $class .= ' current-cat'; 1396 elseif ( isset($_current_category) && $_current_category && ($category->term_id == $_current_category->parent) ) 1397 $class .= ' current-cat-parent'; 1398 $output .= ' class="'.$class.'"'; 1399 $output .= ">$link\n"; 1400 } else { 1401 $output .= "\t$link<br />\n"; 1402 } 1403 } 1404 1405 /** 1406 * @see Walker::end_el() 1407 * @since 2.1.0 1408 * 1409 * @param string $output Passed by reference. Used to append additional content. 1410 * @param object $page Not used. 1411 * @param int $depth Depth of category. Not used. 1412 * @param array $args Only uses 'list' for whether should append to output. 1413 */ 1414 function end_el(&$output, $page, $depth, $args) { 1415 if ( 'list' != $args['style'] ) 1416 return; 1417 1418 $output .= "</li>\n"; 1419 } 1420 1421 } 1422 1423 /** 1424 * Create HTML dropdown list of Categories. 1425 * 1426 * @package WordPress 1427 * @since 2.1.0 1428 * @uses Walker 1429 */ 1430 class Walker_CategoryDropdown extends Walker { 1431 /** 1432 * @see Walker::$tree_type 1433 * @since 2.1.0 1434 * @var string 1435 */ 1436 var $tree_type = 'category'; 1437 1438 /** 1439 * @see Walker::$db_fields 1440 * @since 2.1.0 1441 * @todo Decouple this 1442 * @var array 1443 */ 1444 var $db_fields = array ('parent' => 'parent', 'id' => 'term_id'); 1445 1446 /** 1447 * @see Walker::start_el() 1448 * @since 2.1.0 1449 * 1450 * @param string $output Passed by reference. Used to append additional content. 1451 * @param object $category Category data object. 1452 * @param int $depth Depth of category. Used for padding. 1453 * @param array $args Uses 'selected', 'show_count', and 'show_last_update' keys, if they exist. 1454 */ 1455 function start_el(&$output, $category, $depth, $args) { 1456 $pad = str_repeat(' ', $depth * 3); 1457 1458 $cat_name = apply_filters('list_cats', $category->name, $category); 1459 $output .= "\t<option class=\"level-$depth\" value=\"".$category->term_id."\""; 1460 if ( $category->term_id == $args['selected'] ) 1461 $output .= ' selected="selected"'; 1462 $output .= '>'; 1463 $output .= $pad.$cat_name; 1464 if ( $args['show_count'] ) 1465 $output .= ' ('. $category->count .')'; 1466 if ( $args['show_last_update'] ) { 1467 $format = 'Y-m-d'; 1468 $output .= ' ' . gmdate($format, $category->last_update_timestamp); 1469 } 1470 $output .= "</option>\n"; 1471 } 1472 } 1473 1474 /** 1475 * Send XML response back to AJAX request. 1476 * 1477 * @package WordPress 1478 * @since 2.1.0 1479 */ 1480 class WP_Ajax_Response { 1481 /** 1482 * Store XML responses to send. 1483 * 1484 * @since 2.1.0 1485 * @var array 1486 * @access private 1487 */ 1488 var $responses = array(); 1489 1490 /** 1491 * PHP4 Constructor - Passes args to {@link WP_Ajax_Response::add()}. 1492 * 1493 * @since 2.1.0 1494 * @see WP_Ajax_Response::add() 1495 * 1496 * @param string|array $args Optional. Will be passed to add() method. 1497 * @return WP_Ajax_Response 1498 */ 1499 function WP_Ajax_Response( $args = '' ) { 1500 if ( !empty($args) ) 1501 $this->add($args); 1502 } 1503 1504 /** 1505 * Append to XML response based on given arguments. 1506 * 1507 * The arguments that can be passed in the $args parameter are below. It is 1508 * also possible to pass a WP_Error object in either the 'id' or 'data' 1509 * argument. The parameter isn't actually optional, content should be given 1510 * in order to send the correct response. 1511 * 1512 * 'what' argument is a string that is the XMLRPC response type. 1513 * 'action' argument is a boolean or string that acts like a nonce. 1514 * 'id' argument can be WP_Error or an integer. 1515 * 'old_id' argument is false by default or an integer of the previous ID. 1516 * 'position' argument is an integer or a string with -1 = top, 1 = bottom, 1517 * html ID = after, -html ID = before. 1518 * 'data' argument is a string with the content or message. 1519 * 'supplemental' argument is an array of strings that will be children of 1520 * the supplemental element. 1521 * 1522 * @since 2.1.0 1523 * 1524 * @param string|array $args Override defaults. 1525 * @return string XML response. 1526 */ 1527 function add( $args = '' ) { 1528 $defaults = array( 1529 'what' => 'object', 'action' => false, 1530 'id' => '0', 'old_id' => false, 1531 'position' => 1, 1532 'data' => '', 'supplemental' => array() 1533 ); 1534 1535 $r = wp_parse_args( $args, $defaults ); 1536 extract( $r, EXTR_SKIP ); 1537 $position = preg_replace( '/[^a-z0-9:_-]/i', '', $position ); 1538 1539 if ( is_wp_error($id) ) { 1540 $data = $id; 1541 $id = 0; 1542 } 1543 1544 $response = ''; 1545 if ( is_wp_error($data) ) { 1546 foreach ( (array) $data->get_error_codes() as $code ) { 1547 $response .= "<wp_error code='$code'><![CDATA[" . $data->get_error_message($code) . "]]></wp_error>"; 1548 if ( !$error_data = $data->get_error_data($code) ) 1549 continue; 1550 $class = ''; 1551 if ( is_object($error_data) ) { 1552 $class = ' class="' . get_class($error_data) . '"'; 1553 $error_data = get_object_vars($error_data); 1554 } 1555 1556 $response .= "<wp_error_data code='$code'$class>"; 1557 1558 if ( is_scalar($error_data) ) { 1559 $response .= "<![CDATA[$error_data]]>"; 1560 } elseif ( is_array($error_data) ) { 1561 foreach ( $error_data as $k => $v ) 1562 $response .= "<$k><![CDATA[$v]]></$k>"; 1563 } 1564 1565 $response .= "</wp_error_data>"; 1566 } 1567 } else { 1568 $response = "<response_data><![CDATA[$data]]></response_data>"; 1569 } 1570 1571 $s = ''; 1572 if ( is_array($supplemental) ) { 1573 foreach ( $supplemental as $k => $v ) 1574 $s .= "<$k><![CDATA[$v]]></$k>"; 1575 $s = "<supplemental>$s</supplemental>"; 1576 } 1577 1578 if ( false === $action ) 1579 $action = $_POST['action']; 1580 1581 $x = ''; 1582 $x .= "<response action='{$action}_$id'>"; // The action attribute in the xml output is formatted like a nonce action 1583 $x .= "<$what id='$id' " . ( false === $old_id ? '' : "old_id='$old_id' " ) . "position='$position'>"; 1584 $x .= $response; 1585 $x .= $s; 1586 $x .= "</$what>"; 1587 $x .= "</response>"; 1588 1589 $this->responses[] = $x; 1590 return $x; 1591 } 1592 1593 /** 1594 * Display XML formatted responses. 1595 * 1596 * Sets the content type header to text/xml. 1597 * 1598 * @since 2.1.0 1599 */ 1600 function send() { 1601 header('Content-Type: text/xml'); 1602 echo "<?xml version='1.0' standalone='yes'?><wp_ajax>"; 1603 foreach ( (array) $this->responses as $response ) 1604 echo $response; 1605 echo '</wp_ajax>'; 1606 die(); 1607 } 1608 } 1609 1610 /** 1611 * Helper class to remove the need to use eval to replace $matches[] in query strings. 1612 * 1613 * @since 2.9.0 1614 */ 1615 class WP_MatchesMapRegex { 1616 /** 1617 * store for matches 1618 * 1619 * @access private 1620 * @var array 1621 */ 1622 var $_matches; 1623 1624 /** 1625 * store for mapping result 1626 * 1627 * @access public 1628 * @var string 1629 */ 1630 var $output; 1631 1632 /** 1633 * subject to perform mapping on (query string containing $matches[] references 1634 * 1635 * @access private 1636 * @var string 1637 */ 1638 var $_subject; 1639 1640 /** 1641 * regexp pattern to match $matches[] references 1642 * 1643 * @var string 1644 */ 1645 var $_pattern = '(\$matches\[[1-9]+[0-9]*\])'; // magic number 1646 1647 /** 1648 * constructor 1649 * 1650 * @param string $subject subject if regex 1651 * @param array $matches data to use in map 1652 * @return self 1653 */ 1654 function WP_MatchesMapRegex($subject, $matches) { 1655 $this->_subject = $subject; 1656 $this->_matches = $matches; 1657 $this->output = $this->_map(); 1658 } 1659 1660 /** 1661 * Substitute substring matches in subject. 1662 * 1663 * static helper function to ease use 1664 * 1665 * @access public 1666 * @param string $subject subject 1667 * @param array $matches data used for subsitution 1668 * @return string 1669 */ 1670 function apply($subject, $matches) { 1671 $oSelf =& new WP_MatchesMapRegex($subject, $matches); 1672 return $oSelf->output; 1673 } 1674 1675 /** 1676 * do the actual mapping 1677 * 1678 * @access private 1679 * @return string 1680 */ 1681 function _map() { 1682 $callback = array(&$this, 'callback'); 1683 return preg_replace_callback($this->_pattern, $callback, $this->_subject); 1684 } 1685 1686 /** 1687 * preg_replace_callback hook 1688 * 1689 * @access public 1690 * @param array $matches preg_replace regexp matches 1691 * @return string 1692 */ 1693 function callback($matches) { 1694 $index = intval(substr($matches[0], 9, -1)); 1695 return ( isset( $this->_matches[$index] ) ? urlencode($this->_matches[$index]) : '' ); 1696 } 1697 1698 } 1699 1700 ?>
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Thu Sep 9 03:37:14 2010 | Cross-referenced by PHPXref 0.7 |