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 ); | //Cross Site Scripting
|
376 | foreach( (array) $headers as $name => $field_value ) |
377 | @header("{$name}: {$field_value}"); | //Cross Site Scripting
|
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 $max_depth = 0 means display all levels; $max_depth > 0 specifies the number of display levels. |
984 | * @param $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 ) |