wp/wp-includes/class-wp-list-util.php
changeset 7 cf61fcea0001
child 9 177826044cd9
equal deleted inserted replaced
6:490d5cc509ed 7:cf61fcea0001
       
     1 <?php
       
     2 /**
       
     3  * WordPress List utility class
       
     4  *
       
     5  * @package WordPress
       
     6  * @since 4.7.0
       
     7  */
       
     8 
       
     9 /**
       
    10  * List utility.
       
    11  *
       
    12  * Utility class to handle operations on an array of objects.
       
    13  *
       
    14  * @since 4.7.0
       
    15  */
       
    16 class WP_List_Util {
       
    17 	/**
       
    18 	 * The input array.
       
    19 	 *
       
    20 	 * @since 4.7.0
       
    21 	 * @var array
       
    22 	 */
       
    23 	private $input = array();
       
    24 
       
    25 	/**
       
    26 	 * The output array.
       
    27 	 *
       
    28 	 * @since 4.7.0
       
    29 	 * @var array
       
    30 	 */
       
    31 	private $output = array();
       
    32 
       
    33 	/**
       
    34 	 * Temporary arguments for sorting.
       
    35 	 *
       
    36 	 * @since 4.7.0
       
    37 	 * @var array
       
    38 	 */
       
    39 	private $orderby = array();
       
    40 
       
    41 	/**
       
    42 	 * Constructor.
       
    43 	 *
       
    44 	 * Sets the input array.
       
    45 	 *
       
    46 	 * @since 4.7.0
       
    47 	 *
       
    48 	 * @param array $input Array to perform operations on.
       
    49 	 */
       
    50 	public function __construct( $input ) {
       
    51 		$this->output = $this->input = $input;
       
    52 	}
       
    53 
       
    54 	/**
       
    55 	 * Returns the original input array.
       
    56 	 *
       
    57 	 * @since 4.7.0
       
    58 	 *
       
    59 	 * @return array The input array.
       
    60 	 */
       
    61 	public function get_input() {
       
    62 		return $this->input;
       
    63 	}
       
    64 
       
    65 	/**
       
    66 	 * Returns the output array.
       
    67 	 *
       
    68 	 * @since 4.7.0
       
    69 	 *
       
    70 	 * @return array The output array.
       
    71 	 */
       
    72 	public function get_output() {
       
    73 		return $this->output;
       
    74 	}
       
    75 
       
    76 	/**
       
    77 	 * Filters the list, based on a set of key => value arguments.
       
    78 	 *
       
    79 	 * @since 4.7.0
       
    80 	 *
       
    81 	 * @param array  $args     Optional. An array of key => value arguments to match
       
    82 	 *                         against each object. Default empty array.
       
    83 	 * @param string $operator Optional. The logical operation to perform. 'AND' means
       
    84 	 *                         all elements from the array must match. 'OR' means only
       
    85 	 *                         one element needs to match. 'NOT' means no elements may
       
    86 	 *                         match. Default 'AND'.
       
    87 	 * @return array Array of found values.
       
    88 	 */
       
    89 	public function filter( $args = array(), $operator = 'AND' ) {
       
    90 		if ( empty( $args ) ) {
       
    91 			return $this->output;
       
    92 		}
       
    93 
       
    94 		$operator = strtoupper( $operator );
       
    95 
       
    96 		if ( ! in_array( $operator, array( 'AND', 'OR', 'NOT' ), true ) ) {
       
    97 			return array();
       
    98 		}
       
    99 
       
   100 		$count = count( $args );
       
   101 		$filtered = array();
       
   102 
       
   103 		foreach ( $this->output as $key => $obj ) {
       
   104 			$to_match = (array) $obj;
       
   105 
       
   106 			$matched = 0;
       
   107 			foreach ( $args as $m_key => $m_value ) {
       
   108 				if ( array_key_exists( $m_key, $to_match ) && $m_value == $to_match[ $m_key ] ) {
       
   109 					$matched++;
       
   110 				}
       
   111 			}
       
   112 
       
   113 			if (
       
   114 				( 'AND' == $operator && $matched == $count ) ||
       
   115 				( 'OR' == $operator && $matched > 0 ) ||
       
   116 				( 'NOT' == $operator && 0 == $matched )
       
   117 			) {
       
   118 				$filtered[$key] = $obj;
       
   119 			}
       
   120 		}
       
   121 
       
   122 		$this->output = $filtered;
       
   123 
       
   124 		return $this->output;
       
   125 	}
       
   126 
       
   127 	/**
       
   128 	 * Plucks a certain field out of each object in the list.
       
   129 	 *
       
   130 	 * This has the same functionality and prototype of
       
   131 	 * array_column() (PHP 5.5) but also supports objects.
       
   132 	 *
       
   133 	 * @since 4.7.0
       
   134 	 *
       
   135 	 * @param int|string $field     Field from the object to place instead of the entire object
       
   136 	 * @param int|string $index_key Optional. Field from the object to use as keys for the new array.
       
   137 	 *                              Default null.
       
   138 	 * @return array Array of found values. If `$index_key` is set, an array of found values with keys
       
   139 	 *               corresponding to `$index_key`. If `$index_key` is null, array keys from the original
       
   140 	 *               `$list` will be preserved in the results.
       
   141 	 */
       
   142 	public function pluck( $field, $index_key = null ) {
       
   143 		if ( ! $index_key ) {
       
   144 			/*
       
   145 			 * This is simple. Could at some point wrap array_column()
       
   146 			 * if we knew we had an array of arrays.
       
   147 			 */
       
   148 			foreach ( $this->output as $key => $value ) {
       
   149 				if ( is_object( $value ) ) {
       
   150 					$this->output[ $key ] = $value->$field;
       
   151 				} else {
       
   152 					$this->output[ $key ] = $value[ $field ];
       
   153 				}
       
   154 			}
       
   155 			return $this->output;
       
   156 		}
       
   157 
       
   158 		/*
       
   159 		 * When index_key is not set for a particular item, push the value
       
   160 		 * to the end of the stack. This is how array_column() behaves.
       
   161 		 */
       
   162 		$newlist = array();
       
   163 		foreach ( $this->output as $value ) {
       
   164 			if ( is_object( $value ) ) {
       
   165 				if ( isset( $value->$index_key ) ) {
       
   166 					$newlist[ $value->$index_key ] = $value->$field;
       
   167 				} else {
       
   168 					$newlist[] = $value->$field;
       
   169 				}
       
   170 			} else {
       
   171 				if ( isset( $value[ $index_key ] ) ) {
       
   172 					$newlist[ $value[ $index_key ] ] = $value[ $field ];
       
   173 				} else {
       
   174 					$newlist[] = $value[ $field ];
       
   175 				}
       
   176 			}
       
   177 		}
       
   178 
       
   179 		$this->output = $newlist;
       
   180 
       
   181 		return $this->output;
       
   182 	}
       
   183 
       
   184 	/**
       
   185 	 * Sorts the list, based on one or more orderby arguments.
       
   186 	 *
       
   187 	 * @since 4.7.0
       
   188 	 *
       
   189 	 * @param string|array $orderby       Optional. Either the field name to order by or an array
       
   190 	 *                                    of multiple orderby fields as $orderby => $order.
       
   191 	 * @param string       $order         Optional. Either 'ASC' or 'DESC'. Only used if $orderby
       
   192 	 *                                    is a string.
       
   193 	 * @param bool         $preserve_keys Optional. Whether to preserve keys. Default false.
       
   194 	 * @return array The sorted array.
       
   195 	 */
       
   196 	public function sort( $orderby = array(), $order = 'ASC', $preserve_keys = false ) {
       
   197 		if ( empty( $orderby ) ) {
       
   198 			return $this->output;
       
   199 		}
       
   200 
       
   201 		if ( is_string( $orderby ) ) {
       
   202 			$orderby = array( $orderby => $order );
       
   203 		}
       
   204 
       
   205 		foreach ( $orderby as $field => $direction ) {
       
   206 			$orderby[ $field ] = 'DESC' === strtoupper( $direction ) ? 'DESC' : 'ASC';
       
   207 		}
       
   208 
       
   209 		$this->orderby = $orderby;
       
   210 
       
   211 		if ( $preserve_keys ) {
       
   212 			uasort( $this->output, array( $this, 'sort_callback' ) );
       
   213 		} else {
       
   214 			usort( $this->output, array( $this, 'sort_callback' ) );
       
   215 		}
       
   216 
       
   217 		$this->orderby = array();
       
   218 
       
   219 		return $this->output;
       
   220 	}
       
   221 
       
   222 	/**
       
   223 	 * Callback to sort the list by specific fields.
       
   224 	 *
       
   225 	 * @since 4.7.0
       
   226 	 *
       
   227 	 * @see WP_List_Util::sort()
       
   228 	 *
       
   229 	 * @param object|array $a One object to compare.
       
   230 	 * @param object|array $b The other object to compare.
       
   231 	 * @return int 0 if both objects equal. -1 if second object should come first, 1 otherwise.
       
   232 	 */
       
   233 	private function sort_callback( $a, $b ) {
       
   234 		if ( empty( $this->orderby ) ) {
       
   235 			return 0;
       
   236 		}
       
   237 
       
   238 		$a = (array) $a;
       
   239 		$b = (array) $b;
       
   240 
       
   241 		foreach ( $this->orderby as $field => $direction ) {
       
   242 			if ( ! isset( $a[ $field ] ) || ! isset( $b[ $field ] ) ) {
       
   243 				continue;
       
   244 			}
       
   245 
       
   246 			if ( $a[ $field ] == $b[ $field ] ) {
       
   247 				continue;
       
   248 			}
       
   249 
       
   250 			$results = 'DESC' === $direction ? array( 1, -1 ) : array( -1, 1 );
       
   251 
       
   252 			if ( is_numeric( $a[ $field ] ) && is_numeric( $b[ $field ] ) ) {
       
   253 				return ( $a[ $field ] < $b[ $field ] ) ? $results[0] : $results[1];
       
   254 			}
       
   255 
       
   256 			return 0 > strcmp( $a[ $field ], $b[ $field ] ) ? $results[0] : $results[1];
       
   257 		}
       
   258 
       
   259 		return 0;
       
   260 	}
       
   261 }