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