/wp-content/plugins/sitepress-multilingual-cms/inc/query-filtering/wpml-query-parser.class.php

https://gitlab.com/woxiprogrammers/infinia-wordpress · PHP · 372 lines · 325 code · 15 blank · 32 comment · 74 complexity · 3df8dbaa00d0cfdcb83fc904e1417711 MD5 · raw file

  1. <?php
  2. /**
  3. * Class WPML_Query_Parser
  4. *
  5. * @since 3.2.3
  6. */
  7. class WPML_Query_Parser extends WPML_Full_Translation_API {
  8. /** @var WPML_Query_Filter $query_filter */
  9. private $query_filter;
  10. /**
  11. * @param SitePress $sitepress
  12. * @param wpdb $wpdb
  13. * @param WPML_Post_Translation $post_translation
  14. * @param WPML_Term_Translation $term_translation
  15. * @param WPML_Query_Filter $query_filter
  16. */
  17. public function __construct( &$sitepress, &$wpdb, &$post_translation, &$term_translation, &$query_filter ) {
  18. parent::__construct( $sitepress, $wpdb, $post_translation, $term_translation );
  19. $this->query_filter = &$query_filter;
  20. }
  21. /**
  22. * @param WP_Query $q
  23. *
  24. * @return WP_Query
  25. */
  26. function parse_query( $q ) {
  27. if ( is_admin() ) {
  28. return $q;
  29. }
  30. $current_language = $this->sitepress->get_current_language();
  31. $q = $this->maybe_adjust_name_var( $q );
  32. if ( $current_language !== $this->sitepress->get_default_language() ) {
  33. $cat_array = ! empty( $q->query_vars['cat'] ) ? array_map( 'intval',
  34. array_map( 'trim',
  35. explode( ',',
  36. $q->query_vars['cat'] ) ) ) : array();
  37. if ( ! empty( $q->query_vars['category_name'] ) ) {
  38. $categories = array_filter( array_map( 'trim', explode( ",", $q->query_vars['category_name'] ) ) );
  39. $cat_array = array();
  40. foreach ( $categories as $category ) {
  41. $cat = get_term_by( 'slug', preg_replace( '#((.*)/)#', '', $category ), 'category' );
  42. $cat = $cat ? $cat : get_term_by( 'name', $category, 'category' );
  43. if ( is_object( $cat ) && $cat->term_id ) {
  44. $cat_array[] = $cat->term_id;
  45. }
  46. }
  47. if ( empty( $cat_array ) ) {
  48. $q->query_vars['p'] = - 1;
  49. }
  50. }
  51. if ( ! empty( $q->query_vars['category__and'] ) ) {
  52. $cat_array = $q->query_vars['category__and'];
  53. }
  54. if ( ! empty( $q->query_vars['category__in'] ) ) {
  55. $cat_array = array_unique( array_merge( $cat_array,
  56. array_map( 'intval', $q->query_vars['category__in'] ) ) );
  57. }
  58. if ( ! empty( $q->query_vars['category__not_in'] ) ) {
  59. $__cats = array();
  60. foreach ( $q->query_vars['category__not_in'] as $key => $val ) {
  61. $__cats[ $key ] = - 1 * intval( $val );
  62. }
  63. $cat_array = array_unique( array_merge( $cat_array, $__cats ) );
  64. }
  65. if ( ! empty( $cat_array ) ) {
  66. $translated_ids = array();
  67. foreach ( $cat_array as $c ) {
  68. $sign = intval( $c ) < 0 ? - 1 : 1;
  69. $translated_ids[] = $sign * intval( $this->term_translations->term_id_in( abs( $c ),
  70. $current_language,
  71. true ) );
  72. }
  73. if ( ! empty( $q->query_vars['cat'] ) ) {
  74. $q->query_vars['cat'] = join( ',', $translated_ids );
  75. }
  76. if ( ! empty( $q->query_vars['category_name'] ) ) {
  77. $_ctmp = get_term_by( 'id', $translated_ids[0], 'category' );
  78. $q->query_vars['category_name'] = $_ctmp->slug;
  79. }
  80. if ( ! empty( $q->query_vars['category__and'] ) ) {
  81. $q->query_vars['category__and'] = $translated_ids;
  82. }
  83. if ( ! empty( $q->query_vars['category__in'] ) ) {
  84. $__translated_in = array();
  85. foreach ( $translated_ids as $key => $t_id ) {
  86. if ( $t_id > 0 ) {
  87. $__translated_in[ $key ] = $t_id;
  88. }
  89. }
  90. $q->query_vars['category__in'] = $__translated_in;
  91. }
  92. if ( ! empty( $q->query_vars['category__not_in'] ) ) {
  93. $__translated_not_in = array();
  94. foreach ( $translated_ids as $key => $t_id ) {
  95. if ( $t_id < 0 ) {
  96. $__translated_not_in[ $key ] = $t_id;
  97. }
  98. }
  99. $q->query_vars['category__not_in'] = $__translated_not_in;
  100. }
  101. }
  102. $tag_array = array();
  103. $tag_glue = '';
  104. if ( ! empty( $q->query_vars['tag'] ) ) {
  105. $tag_glue = false !== strpos( $q->query_vars['tag'], ' ' ) ? '+' : ',';
  106. $exp = explode( ' ', $q->query_vars['tag'] );
  107. foreach ( $exp as $e ) {
  108. $tag_array[] = $this->wpdb->get_var( $this->wpdb->prepare( "SELECT x.term_id FROM {$this->wpdb->terms} t
  109. JOIN {$this->wpdb->term_taxonomy} x ON t.term_id=x.term_id WHERE x.taxonomy='post_tag' AND t.slug=%s LIMIT 1",
  110. $e ) );
  111. }
  112. $_tmp = array_unique( $tag_array );
  113. if ( count( $_tmp ) == 1 && empty( $_tmp[0] ) ) {
  114. $tag_array = array();
  115. }
  116. }
  117. if ( ! empty( $q->query_vars['tag_id'] ) ) {
  118. $tag_array = array_map( 'trim', explode( ',', $q->query_vars['tag_id'] ) );
  119. }
  120. foreach ( array( 'tag__not_in', 'tag__in', 'tag__and' ) as $index ) {
  121. if ( ! empty( $q->query_vars[ $index ] ) ) {
  122. $tag_array = $q->query_vars[ $index ];
  123. break;
  124. }
  125. }
  126. // tag_slug__in
  127. if ( ! empty( $q->query_vars['tag_slug__in'] ) ) {
  128. foreach ( $q->query_vars['tag_slug__in'] as $t ) {
  129. if ( $tg = $this->wpdb->get_var( $this->wpdb->prepare( "
  130. SELECT x.term_id FROM {$this->wpdb->terms} t
  131. JOIN {$this->wpdb->term_taxonomy} x ON t.term_id=x.term_id
  132. WHERE x.taxonomy='post_tag' AND t.slug=%s LIMIT 1",
  133. $t ) )
  134. ) {
  135. $tag_array[] = $tg;
  136. }
  137. }
  138. }
  139. if ( ! empty( $q->query_vars['tag_slug__and'] ) ) {
  140. foreach ( $q->query_vars['tag_slug__and'] as $t ) {
  141. $tag_array[] = $this->wpdb->get_var( $this->wpdb->prepare( "SELECT x.term_id FROM {$this->wpdb->terms} t
  142. JOIN {$this->wpdb->term_taxonomy} x ON t.term_id=x.term_id WHERE x.taxonomy='post_tag' AND t.slug=%s LIMIT 1",
  143. $t ) );
  144. }
  145. }
  146. if ( ! empty( $tag_array ) ) {
  147. $translated_ids = array();
  148. foreach ( $tag_array as $c ) {
  149. if ( intval( $c ) < 0 ) {
  150. $sign = - 1;
  151. } else {
  152. $sign = 1;
  153. }
  154. $_tid = intval( $this->term_translations->term_id_in( abs( $c ),
  155. $current_language,
  156. true ) );
  157. $translated_ids[] = $sign * $_tid;
  158. }
  159. }
  160. if ( ! empty( $translated_ids ) ) {
  161. if ( isset( $q->query_vars['tag'] ) && $q->query_vars['tag'] !== "" ) {
  162. $slugs = $this->wpdb->get_col( "SELECT slug
  163. FROM {$this->wpdb->terms}
  164. WHERE term_id IN (" . wpml_prepare_in( $translated_ids,
  165. '%d' ) . ")" );
  166. $q->query_vars['tag'] = join( $tag_glue, $slugs );
  167. }
  168. foreach ( array( 'tag__in', 'tag__and', 'tag_id' ) as $index ) {
  169. if ( ! empty( $q->query_vars[ $index ] ) ) {
  170. $q->query_vars[ $index ] = join( ',', $translated_ids );
  171. break;
  172. }
  173. }
  174. if ( ! empty( $q->query_vars['tag__not_in'] ) ) {
  175. $q->query_vars['tag__not_in'] = array_map( 'abs', $translated_ids );
  176. }
  177. if ( ! empty( $q->query_vars['tag_slug__in'] ) ) {
  178. $q->query_vars['tag_slug__in'] = $this->wpdb->get_col( "SELECT slug
  179. FROM {$this->wpdb->terms}
  180. WHERE term_id IN (" . wpml_prepare_in( $translated_ids,
  181. '%d' ) . ")" );
  182. }
  183. if ( ! empty( $q->query_vars['tag_slug__and'] ) ) {
  184. $q->query_vars['tag_slug__and'] = $this->wpdb->get_col( "SELECT slug
  185. FROM {$this->wpdb->terms}
  186. WHERE term_id IN (" . wpml_prepare_in( $translated_ids,
  187. '%d' ) . ")" );
  188. }
  189. }
  190. $post_type = ! empty( $q->query_vars['post_type'] ) ? $q->query_vars['post_type'] : 'post';
  191. if ( ! is_array( $post_type ) ) {
  192. $post_type = (array) $post_type;
  193. }
  194. if ( ! empty( $q->query_vars['page_id'] ) ) {
  195. $q->query_vars['page_id'] = $this->post_translations->element_id_in( $q->query_vars['page_id'],
  196. $current_language,
  197. true );
  198. $q->query = preg_replace( '/page_id=[0-9]+/',
  199. 'page_id=' . $q->query_vars['page_id'],
  200. $q->query );
  201. }
  202. $q = $this->adjust_query_ids( $q, 'include' );
  203. $q = $this->adjust_query_ids( $q, 'exclude' );
  204. if ( isset( $q->query_vars['p'] ) && ! empty( $q->query_vars['p'] ) ) {
  205. $q->query_vars['p'] = $this->post_translations->element_id_in( $q->query_vars['p'],
  206. $current_language,
  207. true );
  208. }
  209. if ( $this->sitepress->is_translated_post_type( $post_type[0] ) && ! empty( $q->query_vars['name'] ) ) {
  210. if ( is_post_type_hierarchical( $post_type[0] ) ) {
  211. $reqpage = get_page_by_path( $q->query_vars['name'], OBJECT, $post_type[0] );
  212. if ( $reqpage ) {
  213. $q->query_vars['p'] = $this->post_translations->element_id_in( $reqpage->ID,
  214. $current_language,
  215. true );
  216. unset( $q->query_vars['name'] );
  217. // We need to set this to an empty string otherwise WP will derive the pagename from this.
  218. $q->query_vars[ $post_type[0] ] = '';
  219. }
  220. } else {
  221. $pid_prepared = $this->wpdb->prepare( "SELECT ID FROM {$this->wpdb->posts} WHERE post_name=%s AND post_type=%s LIMIT 1",
  222. array( $q->query_vars['name'], $post_type[0] ) );
  223. $pid = $this->wpdb->get_var( $pid_prepared );
  224. if ( ! empty( $pid ) ) {
  225. $q->query_vars['p'] = $this->post_translations->element_id_in( $pid, $current_language, true );
  226. unset( $q->query_vars['name'] );
  227. }
  228. }
  229. }
  230. $q = $this->adjust_q_var_pids( $q, $post_type, 'post__in' );
  231. $q = $this->adjust_q_var_pids( $q, $post_type, 'post__not_in' );
  232. if ( ! empty( $q->query_vars['post_parent'] ) && $q->query_vars['post_type'] !== 'attachment' && $post_type ) {
  233. $q->query_vars['post_parent'] = $this->post_translations->element_id_in( $q->query_vars['post_parent'],
  234. $current_language,
  235. true );
  236. }
  237. if ( isset( $q->query_vars['taxonomy'] ) && $q->query_vars['taxonomy'] ) {
  238. $tax_id = $this->wpdb->get_var( $this->wpdb->prepare( "SELECT term_id FROM {$this->wpdb->terms} WHERE slug=%s LIMIT 1",
  239. $q->query_vars['term'] ) );
  240. if ( $tax_id ) {
  241. $translated_tax_id = $this->term_translations->term_id_in( $tax_id, $current_language, true );
  242. }
  243. if ( isset( $translated_tax_id ) ) {
  244. $q->query_vars['term'] = $this->wpdb->get_var( $this->wpdb->prepare( "SELECT slug FROM {$this->wpdb->terms} WHERE term_id = %d LIMIT 1",
  245. $translated_tax_id ) );
  246. $q->query[ $q->query_vars['taxonomy'] ] = $q->query_vars['term'];
  247. }
  248. }
  249. //TODO: [WPML 3.3] Discuss this. Why WP assumes it's there if query vars are altered? Look at wp-includes/query.php line #2468 search: if ( $this->query_vars_changed ) {
  250. $q->query_vars['meta_query'] = isset( $q->query_vars['meta_query'] ) ? $q->query_vars['meta_query'] : array();
  251. if ( isset( $q->query_vars['tax_query'] ) && is_array( $q->query_vars['tax_query'] ) ) {
  252. foreach ( $q->query['tax_query'] as $num => $fields ) {
  253. if ( ! isset( $fields['terms'] ) ) {
  254. continue;
  255. }
  256. if ( is_array( $fields['terms'] ) ) {
  257. foreach ( $fields['terms'] as $term ) {
  258. $taxonomy = get_term_by( $fields['field'], $term, $fields['taxonomy'] );
  259. if ( is_object( $taxonomy ) ) {
  260. if ( $fields['field'] === 'id' ) {
  261. $field = isset( $taxonomy->term_id ) ? $taxonomy->term_id : null;
  262. } else {
  263. $field = isset( $taxonomy->{$fields['field']} ) ? $taxonomy->{$fields['field']} : null;
  264. }
  265. $tmp = $q->query['tax_query'][ $num ]['terms'];
  266. $tmp = array_diff( (array) $tmp,
  267. array( $term ) ); // removes from array element with original value
  268. $tmp[] = $field;
  269. //Reindex array
  270. $q->query['tax_query'][ $num ]['terms'] = array_values( $tmp );
  271. $tmp = isset( $q->tax_query->queries[ $num ]['terms'] ) ? $q->tax_query->queries[ $num ]['terms'] : array();
  272. $tmp = array_diff( (array) $tmp,
  273. array( $term ) ); // see above
  274. $tmp[] = $field;
  275. //Reindex array
  276. $q->tax_query->queries[ $num ]['terms'] = array_values( $tmp );
  277. $tmp = $q->query_vars['tax_query'][ $num ]['terms'];
  278. $tmp = array_diff( (array) $tmp,
  279. array( $term ) ); // see above
  280. $tmp[] = $field;
  281. //Reindex array
  282. $q->query_vars['tax_query'][ $num ]['terms'] = array_values( $tmp );
  283. unset( $tmp );
  284. }
  285. }
  286. } else if ( is_string( $fields['terms'] ) ) {
  287. $taxonomy = get_term_by( $fields['field'], $fields['terms'], $fields['taxonomy'] );
  288. if ( is_object( $taxonomy ) ) {
  289. $field = isset( $taxonomy->{$fields['field']} ) ? $taxonomy->{$fields['field']} : null;
  290. $q->query['tax_query'][ $num ]['terms'] = $field;
  291. $q->tax_query->queries[ $num ]['terms'][0] = $field;
  292. $q->query_vars['tax_query'][ $num ]['terms'] = $field;
  293. }
  294. }
  295. }
  296. }
  297. }
  298. return $q;
  299. }
  300. /**
  301. * Tries to transform certain queries from "by name" querying to "by ID" to overcome WordPress Core functionality
  302. * for resolving names not being filtered by language
  303. *
  304. * @param WP_Query $q
  305. *
  306. * @return WP_Query
  307. */
  308. private function maybe_adjust_name_var( $q ) {
  309. if ( ( (bool) ( $name_in_q = $q->get( 'name' ) ) === true
  310. || (bool) ( $name_in_q = $q->get( 'pagename' ) ) === true )
  311. && (bool) $q->get( 'page_id' ) === false
  312. || ( (bool) ( $post_type = $q->get('post_type') ) === true
  313. && is_scalar($post_type)
  314. && (bool) ( $name_in_q = $q->get($post_type)) === true ) ) {
  315. list( $name_found, $type, $altered ) = $this->query_filter->get_404_util()->guess_cpt_by_name( $name_in_q,
  316. $q );
  317. if ( $altered === true ) {
  318. $name_before = $q->get( 'name' );
  319. $q->set( 'name', $name_found );
  320. }
  321. $type = $type ? $type : 'page';
  322. $type = is_scalar( $type ) ? $type : ( count( $type ) === 1 ? end( $type ) : false );
  323. $q = $type ? $this->query_filter->get_page_name_filter( $type )->filter_page_name( $q ) : $q;
  324. if ( isset( $name_before ) ) {
  325. $q->set( 'name', $name_before );
  326. }
  327. }
  328. return $q;
  329. }
  330. private function adjust_query_ids( $q, $index ) {
  331. if ( ! empty( $q->query_vars[ $index ] ) ) {
  332. $untranslated = is_array( $q->query_vars[ $index ] ) ? $q->query_vars[ $index ] : explode( ',',
  333. $q->query_vars[ $index ] );
  334. $this->post_translations->prefetch_ids( $untranslated );
  335. $ulanguage_code = $this->sitepress->get_current_language();
  336. $translated = array();
  337. foreach ( $untranslated as $element_id ) {
  338. $translated[] = $this->post_translations->element_id_in( $element_id, $ulanguage_code );
  339. }
  340. $q->query_vars[ $index ] = is_array( $q->query_vars[ $index ] ) ? $translated : implode( ',', $translated );
  341. }
  342. return $q;
  343. }
  344. private function adjust_q_var_pids( $q, $post_types, $index ) {
  345. if ( ! empty( $q->query_vars[ $index ] ) && (bool) $post_types !== false ) {
  346. $untranslated = $q->query_vars[ $index ];
  347. $this->post_translations->prefetch_ids( $untranslated );
  348. $current_lang = $this->sitepress->get_current_language();
  349. $pid = array();
  350. foreach ( $q->query_vars[ $index ] as $p ) {
  351. $pid[] = $this->post_translations->element_id_in( $p, $current_lang, true );
  352. }
  353. $q->query_vars[ $index ] = $pid;
  354. }
  355. return $q;
  356. }
  357. }