Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like WP_Tax_Query often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use WP_Tax_Query, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 22 | class WP_Tax_Query { |
||
| 23 | |||
| 24 | /** |
||
| 25 | * Array of taxonomy queries. |
||
| 26 | * |
||
| 27 | * See WP_Tax_Query::__construct() for information on tax query arguments. |
||
| 28 | * |
||
| 29 | * @since 3.1.0 |
||
| 30 | * @access public |
||
| 31 | * @var array |
||
| 32 | */ |
||
| 33 | public $queries = array(); |
||
| 34 | |||
| 35 | /** |
||
| 36 | * The relation between the queries. Can be one of 'AND' or 'OR'. |
||
| 37 | * |
||
| 38 | * @since 3.1.0 |
||
| 39 | * @access public |
||
| 40 | * @var string |
||
| 41 | */ |
||
| 42 | public $relation; |
||
| 43 | |||
| 44 | /** |
||
| 45 | * Standard response when the query should not return any rows. |
||
| 46 | * |
||
| 47 | * @since 3.2.0 |
||
| 48 | * |
||
| 49 | * @static |
||
| 50 | * @access private |
||
| 51 | * @var string |
||
| 52 | */ |
||
| 53 | private static $no_results = array( 'join' => array( '' ), 'where' => array( '0 = 1' ) ); |
||
| 54 | |||
| 55 | /** |
||
| 56 | * A flat list of table aliases used in the JOIN clauses. |
||
| 57 | * |
||
| 58 | * @since 4.1.0 |
||
| 59 | * @access protected |
||
| 60 | * @var array |
||
| 61 | */ |
||
| 62 | protected $table_aliases = array(); |
||
| 63 | |||
| 64 | /** |
||
| 65 | * Terms and taxonomies fetched by this query. |
||
| 66 | * |
||
| 67 | * We store this data in a flat array because they are referenced in a |
||
| 68 | * number of places by WP_Query. |
||
| 69 | * |
||
| 70 | * @since 4.1.0 |
||
| 71 | * @access public |
||
| 72 | * @var array |
||
| 73 | */ |
||
| 74 | public $queried_terms = array(); |
||
| 75 | |||
| 76 | /** |
||
| 77 | * Database table that where the metadata's objects are stored (eg $wpdb->users). |
||
| 78 | * |
||
| 79 | * @since 4.1.0 |
||
| 80 | * @access public |
||
| 81 | * @var string |
||
| 82 | */ |
||
| 83 | public $primary_table; |
||
| 84 | |||
| 85 | /** |
||
| 86 | * Column in 'primary_table' that represents the ID of the object. |
||
| 87 | * |
||
| 88 | * @since 4.1.0 |
||
| 89 | * @access public |
||
| 90 | * @var string |
||
| 91 | */ |
||
| 92 | public $primary_id_column; |
||
| 93 | |||
| 94 | /** |
||
| 95 | * Constructor. |
||
| 96 | * |
||
| 97 | * @since 3.1.0 |
||
| 98 | * @since 4.1.0 Added support for `$operator` 'NOT EXISTS' and 'EXISTS' values. |
||
| 99 | * @access public |
||
| 100 | * |
||
| 101 | * @param array $tax_query { |
||
| 102 | * Array of taxonomy query clauses. |
||
| 103 | * |
||
| 104 | * @type string $relation Optional. The MySQL keyword used to join |
||
| 105 | * the clauses of the query. Accepts 'AND', or 'OR'. Default 'AND'. |
||
| 106 | * @type array { |
||
| 107 | * Optional. An array of first-order clause parameters, or another fully-formed tax query. |
||
| 108 | * |
||
| 109 | * @type string $taxonomy Taxonomy being queried. Optional when field=term_taxonomy_id. |
||
| 110 | * @type string|int|array $terms Term or terms to filter by. |
||
| 111 | * @type string $field Field to match $terms against. Accepts 'term_id', 'slug', |
||
| 112 | * 'name', or 'term_taxonomy_id'. Default: 'term_id'. |
||
| 113 | * @type string $operator MySQL operator to be used with $terms in the WHERE clause. |
||
| 114 | * Accepts 'AND', 'IN', 'NOT IN', 'EXISTS', 'NOT EXISTS'. |
||
| 115 | * Default: 'IN'. |
||
| 116 | * @type bool $include_children Optional. Whether to include child terms. |
||
| 117 | * Requires a $taxonomy. Default: true. |
||
| 118 | * } |
||
| 119 | * } |
||
| 120 | */ |
||
| 121 | public function __construct( $tax_query ) { |
||
| 130 | |||
| 131 | /** |
||
| 132 | * Ensure the 'tax_query' argument passed to the class constructor is well-formed. |
||
| 133 | * |
||
| 134 | * Ensures that each query-level clause has a 'relation' key, and that |
||
| 135 | * each first-order clause contains all the necessary keys from `$defaults`. |
||
| 136 | * |
||
| 137 | * @since 4.1.0 |
||
| 138 | * @access public |
||
| 139 | * |
||
| 140 | * @param array $queries Array of queries clauses. |
||
| 141 | * @return array Sanitized array of query clauses. |
||
| 142 | */ |
||
| 143 | public function sanitize_query( $queries ) { |
||
| 205 | |||
| 206 | /** |
||
| 207 | * Sanitize a 'relation' operator. |
||
| 208 | * |
||
| 209 | * @since 4.1.0 |
||
| 210 | * @access public |
||
| 211 | * |
||
| 212 | * @param string $relation Raw relation key from the query argument. |
||
| 213 | * @return string Sanitized relation ('AND' or 'OR'). |
||
| 214 | */ |
||
| 215 | public function sanitize_relation( $relation ) { |
||
| 222 | |||
| 223 | /** |
||
| 224 | * Determine whether a clause is first-order. |
||
| 225 | * |
||
| 226 | * A "first-order" clause is one that contains any of the first-order |
||
| 227 | * clause keys ('terms', 'taxonomy', 'include_children', 'field', |
||
| 228 | * 'operator'). An empty clause also counts as a first-order clause, |
||
| 229 | * for backward compatibility. Any clause that doesn't meet this is |
||
| 230 | * determined, by process of elimination, to be a higher-order query. |
||
| 231 | * |
||
| 232 | * @since 4.1.0 |
||
| 233 | * |
||
| 234 | * @static |
||
| 235 | * @access protected |
||
| 236 | * |
||
| 237 | * @param array $query Tax query arguments. |
||
| 238 | * @return bool Whether the query clause is a first-order clause. |
||
| 239 | */ |
||
| 240 | protected static function is_first_order_clause( $query ) { |
||
| 243 | |||
| 244 | /** |
||
| 245 | * Generates SQL clauses to be appended to a main query. |
||
| 246 | * |
||
| 247 | * @since 3.1.0 |
||
| 248 | * |
||
| 249 | * @static |
||
| 250 | * @access public |
||
| 251 | * |
||
| 252 | * @param string $primary_table Database table where the object being filtered is stored (eg wp_users). |
||
| 253 | * @param string $primary_id_column ID column for the filtered object in $primary_table. |
||
| 254 | * @return array { |
||
|
|
|||
| 255 | * Array containing JOIN and WHERE SQL clauses to append to the main query. |
||
| 256 | * |
||
| 257 | * @type string $join SQL fragment to append to the main JOIN clause. |
||
| 258 | * @type string $where SQL fragment to append to the main WHERE clause. |
||
| 259 | * } |
||
| 260 | */ |
||
| 261 | public function get_sql( $primary_table, $primary_id_column ) { |
||
| 267 | |||
| 268 | /** |
||
| 269 | * Generate SQL clauses to be appended to a main query. |
||
| 270 | * |
||
| 271 | * Called by the public WP_Tax_Query::get_sql(), this method |
||
| 272 | * is abstracted out to maintain parity with the other Query classes. |
||
| 273 | * |
||
| 274 | * @since 4.1.0 |
||
| 275 | * @access protected |
||
| 276 | * |
||
| 277 | * @return array { |
||
| 278 | * Array containing JOIN and WHERE SQL clauses to append to the main query. |
||
| 279 | * |
||
| 280 | * @type string $join SQL fragment to append to the main JOIN clause. |
||
| 281 | * @type string $where SQL fragment to append to the main WHERE clause. |
||
| 282 | * } |
||
| 283 | */ |
||
| 284 | View Code Duplication | protected function get_sql_clauses() { |
|
| 298 | |||
| 299 | /** |
||
| 300 | * Generate SQL clauses for a single query array. |
||
| 301 | * |
||
| 302 | * If nested subqueries are found, this method recurses the tree to |
||
| 303 | * produce the properly nested SQL. |
||
| 304 | * |
||
| 305 | * @since 4.1.0 |
||
| 306 | * @access protected |
||
| 307 | * |
||
| 308 | * @param array $query Query to parse, passed by reference. |
||
| 309 | * @param int $depth Optional. Number of tree levels deep we currently are. |
||
| 310 | * Used to calculate indentation. Default 0. |
||
| 311 | * @return array { |
||
| 312 | * Array containing JOIN and WHERE SQL clauses to append to a single query array. |
||
| 313 | * |
||
| 314 | * @type string $join SQL fragment to append to the main JOIN clause. |
||
| 315 | * @type string $where SQL fragment to append to the main WHERE clause. |
||
| 316 | * } |
||
| 317 | */ |
||
| 318 | View Code Duplication | protected function get_sql_for_query( &$query, $depth = 0 ) { |
|
| 383 | |||
| 384 | /** |
||
| 385 | * Generate SQL JOIN and WHERE clauses for a "first-order" query clause. |
||
| 386 | * |
||
| 387 | * @since 4.1.0 |
||
| 388 | * @access public |
||
| 389 | * |
||
| 390 | * @global wpdb $wpdb The WordPress database abstraction object. |
||
| 391 | * |
||
| 392 | * @param array $clause Query clause, passed by reference. |
||
| 393 | * @param array $parent_query Parent query array. |
||
| 394 | * @return array { |
||
| 395 | * Array containing JOIN and WHERE SQL clauses to append to a first-order query. |
||
| 396 | * |
||
| 397 | * @type string $join SQL fragment to append to the main JOIN clause. |
||
| 398 | * @type string $where SQL fragment to append to the main WHERE clause. |
||
| 399 | * } |
||
| 400 | */ |
||
| 401 | public function get_sql_for_clause( &$clause, $parent_query ) { |
||
| 499 | |||
| 500 | /** |
||
| 501 | * Identify an existing table alias that is compatible with the current query clause. |
||
| 502 | * |
||
| 503 | * We avoid unnecessary table joins by allowing each clause to look for |
||
| 504 | * an existing table alias that is compatible with the query that it |
||
| 505 | * needs to perform. |
||
| 506 | * |
||
| 507 | * An existing alias is compatible if (a) it is a sibling of `$clause` |
||
| 508 | * (ie, it's under the scope of the same relation), and (b) the combination |
||
| 509 | * of operator and relation between the clauses allows for a shared table |
||
| 510 | * join. In the case of WP_Tax_Query, this only applies to 'IN' |
||
| 511 | * clauses that are connected by the relation 'OR'. |
||
| 512 | * |
||
| 513 | * @since 4.1.0 |
||
| 514 | * @access protected |
||
| 515 | * |
||
| 516 | * @param array $clause Query clause. |
||
| 517 | * @param array $parent_query Parent query of $clause. |
||
| 518 | * @return string|false Table alias if found, otherwise false. |
||
| 519 | */ |
||
| 520 | protected function find_compatible_table_alias( $clause, $parent_query ) { |
||
| 553 | |||
| 554 | /** |
||
| 555 | * Validates a single query. |
||
| 556 | * |
||
| 557 | * @since 3.2.0 |
||
| 558 | * @access private |
||
| 559 | * |
||
| 560 | * @param array $query The single query. Passed by reference. |
||
| 561 | */ |
||
| 562 | private function clean_query( &$query ) { |
||
| 594 | |||
| 595 | /** |
||
| 596 | * Transforms a single query, from one field to another. |
||
| 597 | * |
||
| 598 | * @since 3.2.0 |
||
| 599 | * |
||
| 600 | * @global wpdb $wpdb The WordPress database abstraction object. |
||
| 601 | * |
||
| 602 | * @param array $query The single query. Passed by reference. |
||
| 603 | * @param string $resulting_field The resulting field. Accepts 'slug', 'name', 'term_taxonomy_id', |
||
| 604 | * or 'term_id'. Default 'term_id'. |
||
| 605 | */ |
||
| 606 | public function transform_query( &$query, $resulting_field ) { |
||
| 670 | } |
||
| 671 |
This check looks for the generic type
arrayas a return type and suggests a more specific type. This type is inferred from the actual code.