Changeset 33749 for trunk/src/wp-includes/user-functions.php
- Timestamp:
- 08/26/2015 04:19:32 AM (10 years ago)
- File:
-
- 1 copied
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/wp-includes/user-functions.php
r33746 r33749 447 447 $option_name = $wpdb->get_blog_prefix() . $option_name; 448 448 return delete_user_meta( $user_id, $option_name ); 449 }450 451 /**452 * WordPress User Query class.453 *454 * @since 3.1.0455 *456 * @see WP_User_Query::prepare_query() for information on accepted arguments.457 */458 class WP_User_Query {459 460 /**461 * Query vars, after parsing462 *463 * @since 3.5.0464 * @access public465 * @var array466 */467 public $query_vars = array();468 469 /**470 * List of found user ids471 *472 * @since 3.1.0473 * @access private474 * @var array475 */476 private $results;477 478 /**479 * Total number of found users for the current query480 *481 * @since 3.1.0482 * @access private483 * @var int484 */485 private $total_users = 0;486 487 /**488 * Metadata query container.489 *490 * @since 4.2.0491 * @access public492 * @var object WP_Meta_Query493 */494 public $meta_query = false;495 496 private $compat_fields = array( 'results', 'total_users' );497 498 // SQL clauses499 public $query_fields;500 public $query_from;501 public $query_where;502 public $query_orderby;503 public $query_limit;504 505 /**506 * PHP5 constructor.507 *508 * @since 3.1.0509 *510 * @param null|string|array $args Optional. The query variables.511 */512 public function __construct( $query = null ) {513 if ( ! empty( $query ) ) {514 $this->prepare_query( $query );515 $this->query();516 }517 }518 519 /**520 * Prepare the query variables.521 *522 * @since 3.1.0523 * @since 4.2.0 Added 'meta_value_num' support for `$orderby` parameter. Added multi-dimensional array syntax524 * for `$orderby` parameter.525 * @since 4.3.0 Added 'has_published_posts' parameter.526 * @access public527 *528 * @global wpdb $wpdb529 * @global int $blog_id530 *531 * @param string|array $query {532 * Optional. Array or string of Query parameters.533 *534 * @type int $blog_id The site ID. Default is the global blog id.535 * @type string $role Role name. Default empty.536 * @type string $meta_key User meta key. Default empty.537 * @type string $meta_value User meta value. Default empty.538 * @type string $meta_compare Comparison operator to test the `$meta_value`. Accepts '=', '!=',539 * '>', '>=', '<', '<=', 'LIKE', 'NOT LIKE', 'IN', 'NOT IN',540 * 'BETWEEN', 'NOT BETWEEN', 'EXISTS', 'NOT EXISTS', 'REGEXP',541 * 'NOT REGEXP', or 'RLIKE'. Default '='.542 * @type array $include An array of user IDs to include. Default empty array.543 * @type array $exclude An array of user IDs to exclude. Default empty array.544 * @type string $search Search keyword. Searches for possible string matches on columns.545 * When `$search_columns` is left empty, it tries to determine which546 * column to search in based on search string. Default empty.547 * @type array $search_columns Array of column names to be searched. Accepts 'ID', 'login',548 * 'nicename', 'email', 'url'. Default empty array.549 * @type string|array $orderby Field(s) to sort the retrieved users by. May be a single value,550 * an array of values, or a multi-dimensional array with fields as551 * keys and orders ('ASC' or 'DESC') as values. Accepted values are552 * 'ID', 'display_name' (or 'name'), 'user_login' (or 'login'),553 * 'user_nicename' (or 'nicename'), 'user_email' (or 'email'),554 * 'user_url' (or 'url'), 'user_registered' (or 'registered'),555 * 'post_count', 'meta_value', 'meta_value_num', the value of556 * `$meta_key`, or an array key of `$meta_query`. To use557 * 'meta_value' or 'meta_value_num', `$meta_key` must be also be558 * defined. Default 'user_login'.559 * @type string $order Designates ascending or descending order of users. Order values560 * passed as part of an `$orderby` array take precedence over this561 * parameter. Accepts 'ASC', 'DESC'. Default 'ASC'.562 * @type int $offset Number of users to offset in retrieved results. Can be used in563 * conjunction with pagination. Default 0.564 * @type int $number Number of users to limit the query for. Can be used in565 * conjunction with pagination. Value -1 (all) is not supported.566 * Default empty (all users).567 * @type bool $count_total Whether to count the total number of users found. If pagination568 * is not needed, setting this to false can improve performance.569 * Default true.570 * @type string|array $fields Which fields to return. Single or all fields (string), or array571 * of fields. Accepts 'ID', 'display_name', 'login', 'nicename',572 * 'email', 'url', 'registered'. Use 'all' for all fields and573 * 'all_with_meta' to include meta fields. Default 'all'.574 * @type string $who Type of users to query. Accepts 'authors'.575 * Default empty (all users).576 * @type bool|array $has_published_posts Pass an array of post types to filter results to users who have577 * published posts in those post types. `true` is an alias for all578 * public post types.579 * }580 */581 public function prepare_query( $query = array() ) {582 global $wpdb;583 584 if ( empty( $this->query_vars ) || ! empty( $query ) ) {585 $this->query_limit = null;586 $this->query_vars = wp_parse_args( $query, array(587 'blog_id' => $GLOBALS['blog_id'],588 'role' => '',589 'meta_key' => '',590 'meta_value' => '',591 'meta_compare' => '',592 'include' => array(),593 'exclude' => array(),594 'search' => '',595 'search_columns' => array(),596 'orderby' => 'login',597 'order' => 'ASC',598 'offset' => '',599 'number' => '',600 'count_total' => true,601 'fields' => 'all',602 'who' => '',603 'has_published_posts' => null,604 ) );605 }606 607 /**608 * Fires before the WP_User_Query has been parsed.609 *610 * The passed WP_User_Query object contains the query variables, not611 * yet passed into SQL.612 *613 * @since 4.0.0614 *615 * @param WP_User_Query $this The current WP_User_Query instance,616 * passed by reference.617 */618 do_action( 'pre_get_users', $this );619 620 $qv =& $this->query_vars;621 622 if ( is_array( $qv['fields'] ) ) {623 $qv['fields'] = array_unique( $qv['fields'] );624 625 $this->query_fields = array();626 foreach ( $qv['fields'] as $field ) {627 $field = 'ID' === $field ? 'ID' : sanitize_key( $field );628 $this->query_fields[] = "$wpdb->users.$field";629 }630 $this->query_fields = implode( ',', $this->query_fields );631 } elseif ( 'all' == $qv['fields'] ) {632 $this->query_fields = "$wpdb->users.*";633 } else {634 $this->query_fields = "$wpdb->users.ID";635 }636 637 if ( isset( $qv['count_total'] ) && $qv['count_total'] )638 $this->query_fields = 'SQL_CALC_FOUND_ROWS ' . $this->query_fields;639 640 $this->query_from = "FROM $wpdb->users";641 $this->query_where = "WHERE 1=1";642 643 // Parse and sanitize 'include', for use by 'orderby' as well as 'include' below.644 if ( ! empty( $qv['include'] ) ) {645 $include = wp_parse_id_list( $qv['include'] );646 } else {647 $include = false;648 }649 650 $blog_id = 0;651 if ( isset( $qv['blog_id'] ) ) {652 $blog_id = absint( $qv['blog_id'] );653 }654 655 if ( isset( $qv['who'] ) && 'authors' == $qv['who'] && $blog_id ) {656 $qv['meta_key'] = $wpdb->get_blog_prefix( $blog_id ) . 'user_level';657 $qv['meta_value'] = 0;658 $qv['meta_compare'] = '!=';659 $qv['blog_id'] = $blog_id = 0; // Prevent extra meta query660 }661 662 if ( $qv['has_published_posts'] && $blog_id ) {663 if ( true === $qv['has_published_posts'] ) {664 $post_types = get_post_types( array( 'public' => true ) );665 } else {666 $post_types = (array) $qv['has_published_posts'];667 }668 669 foreach ( $post_types as &$post_type ) {670 $post_type = $wpdb->prepare( '%s', $post_type );671 }672 673 $posts_table = $wpdb->get_blog_prefix( $blog_id ) . 'posts';674 $this->query_where .= " AND $wpdb->users.ID IN ( SELECT DISTINCT $posts_table.post_author FROM $posts_table WHERE $posts_table.post_status = 'publish' AND $posts_table.post_type IN ( " . join( ", ", $post_types ) . " ) )";675 }676 677 // Meta query.678 $this->meta_query = new WP_Meta_Query();679 $this->meta_query->parse_query_vars( $qv );680 681 $role = '';682 if ( isset( $qv['role'] ) ) {683 $role = trim( $qv['role'] );684 }685 686 if ( $blog_id && ( $role || is_multisite() ) ) {687 $cap_meta_query = array();688 $cap_meta_query['key'] = $wpdb->get_blog_prefix( $blog_id ) . 'capabilities';689 690 if ( $role ) {691 $cap_meta_query['value'] = '"' . $role . '"';692 $cap_meta_query['compare'] = 'like';693 }694 695 if ( empty( $this->meta_query->queries ) ) {696 $this->meta_query->queries = array( $cap_meta_query );697 } elseif ( ! in_array( $cap_meta_query, $this->meta_query->queries, true ) ) {698 // Append the cap query to the original queries and reparse the query.699 $this->meta_query->queries = array(700 'relation' => 'AND',701 array( $this->meta_query->queries, $cap_meta_query ),702 );703 }704 705 $this->meta_query->parse_query_vars( $this->meta_query->queries );706 }707 708 if ( ! empty( $this->meta_query->queries ) ) {709 $clauses = $this->meta_query->get_sql( 'user', $wpdb->users, 'ID', $this );710 $this->query_from .= $clauses['join'];711 $this->query_where .= $clauses['where'];712 713 if ( $this->meta_query->has_or_relation() ) {714 $this->query_fields = 'DISTINCT ' . $this->query_fields;715 }716 }717 718 // sorting719 $qv['order'] = isset( $qv['order'] ) ? strtoupper( $qv['order'] ) : '';720 $order = $this->parse_order( $qv['order'] );721 722 if ( empty( $qv['orderby'] ) ) {723 // Default order is by 'user_login'.724 $ordersby = array( 'user_login' => $order );725 } elseif ( is_array( $qv['orderby'] ) ) {726 $ordersby = $qv['orderby'];727 } else {728 // 'orderby' values may be a comma- or space-separated list.729 $ordersby = preg_split( '/[,\s]+/', $qv['orderby'] );730 }731 732 $orderby_array = array();733 foreach ( $ordersby as $_key => $_value ) {734 if ( ! $_value ) {735 continue;736 }737 738 if ( is_int( $_key ) ) {739 // Integer key means this is a flat array of 'orderby' fields.740 $_orderby = $_value;741 $_order = $order;742 } else {743 // Non-integer key means this the key is the field and the value is ASC/DESC.744 $_orderby = $_key;745 $_order = $_value;746 }747 748 $parsed = $this->parse_orderby( $_orderby );749 750 if ( ! $parsed ) {751 continue;752 }753 754 $orderby_array[] = $parsed . ' ' . $this->parse_order( $_order );755 }756 757 // If no valid clauses were found, order by user_login.758 if ( empty( $orderby_array ) ) {759 $orderby_array[] = "user_login $order";760 }761 762 $this->query_orderby = 'ORDER BY ' . implode( ', ', $orderby_array );763 764 // limit765 if ( isset( $qv['number'] ) && $qv['number'] ) {766 if ( $qv['offset'] )767 $this->query_limit = $wpdb->prepare("LIMIT %d, %d", $qv['offset'], $qv['number']);768 else769 $this->query_limit = $wpdb->prepare("LIMIT %d", $qv['number']);770 }771 772 $search = '';773 if ( isset( $qv['search'] ) )774 $search = trim( $qv['search'] );775 776 if ( $search ) {777 $leading_wild = ( ltrim($search, '*') != $search );778 $trailing_wild = ( rtrim($search, '*') != $search );779 if ( $leading_wild && $trailing_wild )780 $wild = 'both';781 elseif ( $leading_wild )782 $wild = 'leading';783 elseif ( $trailing_wild )784 $wild = 'trailing';785 else786 $wild = false;787 if ( $wild )788 $search = trim($search, '*');789 790 $search_columns = array();791 if ( $qv['search_columns'] )792 $search_columns = array_intersect( $qv['search_columns'], array( 'ID', 'user_login', 'user_email', 'user_url', 'user_nicename' ) );793 if ( ! $search_columns ) {794 if ( false !== strpos( $search, '@') )795 $search_columns = array('user_email');796 elseif ( is_numeric($search) )797 $search_columns = array('user_login', 'ID');798 elseif ( preg_match('|^https?://|', $search) && ! ( is_multisite() && wp_is_large_network( 'users' ) ) )799 $search_columns = array('user_url');800 else801 $search_columns = array('user_login', 'user_url', 'user_email', 'user_nicename', 'display_name');802 }803 804 /**805 * Filter the columns to search in a WP_User_Query search.806 *807 * The default columns depend on the search term, and include 'user_email',808 * 'user_login', 'ID', 'user_url', 'display_name', and 'user_nicename'.809 *810 * @since 3.6.0811 *812 * @param array $search_columns Array of column names to be searched.813 * @param string $search Text being searched.814 * @param WP_User_Query $this The current WP_User_Query instance.815 */816 $search_columns = apply_filters( 'user_search_columns', $search_columns, $search, $this );817 818 $this->query_where .= $this->get_search_sql( $search, $search_columns, $wild );819 }820 821 if ( ! empty( $include ) ) {822 // Sanitized earlier.823 $ids = implode( ',', $include );824 $this->query_where .= " AND $wpdb->users.ID IN ($ids)";825 } elseif ( ! empty( $qv['exclude'] ) ) {826 $ids = implode( ',', wp_parse_id_list( $qv['exclude'] ) );827 $this->query_where .= " AND $wpdb->users.ID NOT IN ($ids)";828 }829 830 // Date queries are allowed for the user_registered field.831 if ( ! empty( $qv['date_query'] ) && is_array( $qv['date_query'] ) ) {832 $date_query = new WP_Date_Query( $qv['date_query'], 'user_registered' );833 $this->query_where .= $date_query->get_sql();834 }835 836 /**837 * Fires after the WP_User_Query has been parsed, and before838 * the query is executed.839 *840 * The passed WP_User_Query object contains SQL parts formed841 * from parsing the given query.842 *843 * @since 3.1.0844 *845 * @param WP_User_Query $this The current WP_User_Query instance,846 * passed by reference.847 */848 do_action_ref_array( 'pre_user_query', array( &$this ) );849 }850 851 /**852 * Execute the query, with the current variables.853 *854 * @since 3.1.0855 *856 * @global wpdb $wpdb WordPress database object for queries.857 */858 public function query() {859 global $wpdb;860 861 $qv =& $this->query_vars;862 863 $query = "SELECT $this->query_fields $this->query_from $this->query_where $this->query_orderby $this->query_limit";864 865 if ( is_array( $qv['fields'] ) || 'all' == $qv['fields'] ) {866 $this->results = $wpdb->get_results( $query );867 } else {868 $this->results = $wpdb->get_col( $query );869 }870 871 /**872 * Filter SELECT FOUND_ROWS() query for the current WP_User_Query instance.873 *874 * @since 3.2.0875 *876 * @global wpdb $wpdb WordPress database abstraction object.877 *878 * @param string $sql The SELECT FOUND_ROWS() query for the current WP_User_Query.879 */880 if ( isset( $qv['count_total'] ) && $qv['count_total'] )881 $this->total_users = $wpdb->get_var( apply_filters( 'found_users_query', 'SELECT FOUND_ROWS()' ) );882 883 if ( !$this->results )884 return;885 886 if ( 'all_with_meta' == $qv['fields'] ) {887 cache_users( $this->results );888 889 $r = array();890 foreach ( $this->results as $userid )891 $r[ $userid ] = new WP_User( $userid, '', $qv['blog_id'] );892 893 $this->results = $r;894 } elseif ( 'all' == $qv['fields'] ) {895 foreach ( $this->results as $key => $user ) {896 $this->results[ $key ] = new WP_User( $user, '', $qv['blog_id'] );897 }898 }899 }900 901 /**902 * Retrieve query variable.903 *904 * @since 3.5.0905 * @access public906 *907 * @param string $query_var Query variable key.908 * @return mixed909 */910 public function get( $query_var ) {911 if ( isset( $this->query_vars[$query_var] ) )912 return $this->query_vars[$query_var];913 914 return null;915 }916 917 /**918 * Set query variable.919 *920 * @since 3.5.0921 * @access public922 *923 * @param string $query_var Query variable key.924 * @param mixed $value Query variable value.925 */926 public function set( $query_var, $value ) {927 $this->query_vars[$query_var] = $value;928 }929 930 /**931 * Used internally to generate an SQL string for searching across multiple columns932 *933 * @access protected934 * @since 3.1.0935 *936 * @global wpdb $wpdb937 *938 * @param string $string939 * @param array $cols940 * @param bool $wild Whether to allow wildcard searches. Default is false for Network Admin, true for single site.941 * Single site allows leading and trailing wildcards, Network Admin only trailing.942 * @return string943 */944 protected function get_search_sql( $string, $cols, $wild = false ) {945 global $wpdb;946 947 $searches = array();948 $leading_wild = ( 'leading' == $wild || 'both' == $wild ) ? '%' : '';949 $trailing_wild = ( 'trailing' == $wild || 'both' == $wild ) ? '%' : '';950 $like = $leading_wild . $wpdb->esc_like( $string ) . $trailing_wild;951 952 foreach ( $cols as $col ) {953 if ( 'ID' == $col ) {954 $searches[] = $wpdb->prepare( "$col = %s", $string );955 } else {956 $searches[] = $wpdb->prepare( "$col LIKE %s", $like );957 }958 }959 960 return ' AND (' . implode(' OR ', $searches) . ')';961 }962 963 /**964 * Return the list of users.965 *966 * @since 3.1.0967 * @access public968 *969 * @return array Array of results.970 */971 public function get_results() {972 return $this->results;973 }974 975 /**976 * Return the total number of users for the current query.977 *978 * @since 3.1.0979 * @access public980 *981 * @return int Number of total users.982 */983 public function get_total() {984 return $this->total_users;985 }986 987 /**988 * Parse and sanitize 'orderby' keys passed to the user query.989 *990 * @since 4.2.0991 * @access protected992 *993 * @global wpdb $wpdb WordPress database abstraction object.994 *995 * @param string $orderby Alias for the field to order by.996 * @return string Value to used in the ORDER clause, if `$orderby` is valid.997 */998 protected function parse_orderby( $orderby ) {999 global $wpdb;1000 1001 $meta_query_clauses = $this->meta_query->get_clauses();1002 1003 $_orderby = '';1004 if ( in_array( $orderby, array( 'login', 'nicename', 'email', 'url', 'registered' ) ) ) {1005 $_orderby = 'user_' . $orderby;1006 } elseif ( in_array( $orderby, array( 'user_login', 'user_nicename', 'user_email', 'user_url', 'user_registered' ) ) ) {1007 $_orderby = $orderby;1008 } elseif ( 'name' == $orderby || 'display_name' == $orderby ) {1009 $_orderby = 'display_name';1010 } elseif ( 'post_count' == $orderby ) {1011 // todo: avoid the JOIN1012 $where = get_posts_by_author_sql( 'post' );1013 $this->query_from .= " LEFT OUTER JOIN (1014 SELECT post_author, COUNT(*) as post_count1015 FROM $wpdb->posts1016 $where1017 GROUP BY post_author1018 ) p ON ({$wpdb->users}.ID = p.post_author)1019 ";1020 $_orderby = 'post_count';1021 } elseif ( 'ID' == $orderby || 'id' == $orderby ) {1022 $_orderby = 'ID';1023 } elseif ( 'meta_value' == $orderby || $this->get( 'meta_key' ) == $orderby ) {1024 $_orderby = "$wpdb->usermeta.meta_value";1025 } elseif ( 'meta_value_num' == $orderby ) {1026 $_orderby = "$wpdb->usermeta.meta_value+0";1027 } elseif ( 'include' === $orderby && ! empty( $this->query_vars['include'] ) ) {1028 $include = wp_parse_id_list( $this->query_vars['include'] );1029 $include_sql = implode( ',', $include );1030 $_orderby = "FIELD( $wpdb->users.ID, $include_sql )";1031 } elseif ( isset( $meta_query_clauses[ $orderby ] ) ) {1032 $meta_clause = $meta_query_clauses[ $orderby ];1033 $_orderby = sprintf( "CAST(%s.meta_value AS %s)", esc_sql( $meta_clause['alias'] ), esc_sql( $meta_clause['cast'] ) );1034 }1035 1036 return $_orderby;1037 }1038 1039 /**1040 * Parse an 'order' query variable and cast it to ASC or DESC as necessary.1041 *1042 * @since 4.2.01043 * @access protected1044 *1045 * @param string $order The 'order' query variable.1046 * @return string The sanitized 'order' query variable.1047 */1048 protected function parse_order( $order ) {1049 if ( ! is_string( $order ) || empty( $order ) ) {1050 return 'DESC';1051 }1052 1053 if ( 'ASC' === strtoupper( $order ) ) {1054 return 'ASC';1055 } else {1056 return 'DESC';1057 }1058 }1059 1060 /**1061 * Make private properties readable for backwards compatibility.1062 *1063 * @since 4.0.01064 * @access public1065 *1066 * @param string $name Property to get.1067 * @return mixed Property.1068 */1069 public function __get( $name ) {1070 if ( in_array( $name, $this->compat_fields ) ) {1071 return $this->$name;1072 }1073 }1074 1075 /**1076 * Make private properties settable for backwards compatibility.1077 *1078 * @since 4.0.01079 * @access public1080 *1081 * @param string $name Property to check if set.1082 * @param mixed $value Property value.1083 * @return mixed Newly-set property.1084 */1085 public function __set( $name, $value ) {1086 if ( in_array( $name, $this->compat_fields ) ) {1087 return $this->$name = $value;1088 }1089 }1090 1091 /**1092 * Make private properties checkable for backwards compatibility.1093 *1094 * @since 4.0.01095 * @access public1096 *1097 * @param string $name Property to check if set.1098 * @return bool Whether the property is set.1099 */1100 public function __isset( $name ) {1101 if ( in_array( $name, $this->compat_fields ) ) {1102 return isset( $this->$name );1103 }1104 }1105 1106 /**1107 * Make private properties un-settable for backwards compatibility.1108 *1109 * @since 4.0.01110 * @access public1111 *1112 * @param string $name Property to unset.1113 */1114 public function __unset( $name ) {1115 if ( in_array( $name, $this->compat_fields ) ) {1116 unset( $this->$name );1117 }1118 }1119 1120 /**1121 * Make private/protected methods readable for backwards compatibility.1122 *1123 * @since 4.0.01124 * @access public1125 *1126 * @param callable $name Method to call.1127 * @param array $arguments Arguments to pass when calling.1128 * @return mixed Return value of the callback, false otherwise.1129 */1130 public function __call( $name, $arguments ) {1131 if ( 'get_search_sql' === $name ) {1132 return call_user_func_array( array( $this, $name ), $arguments );1133 }1134 return false;1135 }1136 449 } 1137 450
Note: See TracChangeset
for help on using the changeset viewer.