diff --git src/wp-includes/class-wp-term-query.php src/wp-includes/class-wp-term-query.php
index 2f3da47301..c4545eda75 100644
|
|
class WP_Term_Query { |
585 | 585 | |
586 | 586 | $selects = array(); |
587 | 587 | switch ( $args['fields'] ) { |
588 | | case 'all': |
589 | | case 'all_with_object_id' : |
590 | | case 'tt_ids' : |
591 | | case 'slugs' : |
592 | | $selects = array( 't.*', 'tt.*' ); |
593 | | if ( 'all_with_object_id' === $args['fields'] && ! empty( $args['object_ids'] ) ) { |
594 | | $selects[] = 'tr.object_id'; |
595 | | } |
596 | | break; |
597 | | case 'ids': |
598 | | case 'id=>parent': |
599 | | $selects = array( 't.term_id', 'tt.parent', 'tt.count', 'tt.taxonomy' ); |
600 | | break; |
601 | | case 'names': |
602 | | $selects = array( 't.term_id', 'tt.parent', 'tt.count', 't.name', 'tt.taxonomy' ); |
603 | | break; |
604 | 588 | case 'count': |
605 | 589 | $orderby = ''; |
606 | 590 | $order = ''; |
607 | 591 | $selects = array( 'COUNT(*)' ); |
608 | 592 | break; |
609 | | case 'id=>name': |
610 | | $selects = array( 't.term_id', 't.name', 'tt.count', 'tt.taxonomy' ); |
| 593 | case 'all_with_object_id' : |
| 594 | $selects = array( 't.term_id', 'tr.object_id' ); |
611 | 595 | break; |
612 | | case 'id=>slug': |
613 | | $selects = array( 't.term_id', 't.slug', 'tt.count', 'tt.taxonomy' ); |
| 596 | |
| 597 | default: |
| 598 | $selects = array( 't.term_id' ); |
614 | 599 | break; |
615 | 600 | } |
616 | 601 | |
… |
… |
class WP_Term_Query { |
672 | 657 | |
673 | 658 | $this->request = "{$this->sql_clauses['select']} {$this->sql_clauses['from']} {$where} {$this->sql_clauses['orderby']} {$this->sql_clauses['limits']}"; |
674 | 659 | |
675 | | // $args can be anything. Only use the args defined in defaults to compute the key. |
676 | | $key = md5( serialize( wp_array_slice_assoc( $args, array_keys( $this->query_var_defaults ) ) ) . serialize( $taxonomies ) . $this->request ); |
| 660 | $key = md5( $this->request ); |
677 | 661 | $last_changed = wp_cache_get_last_changed( 'terms' ); |
678 | 662 | $cache_key = "get_terms:$key:$last_changed"; |
| 663 | |
679 | 664 | $cache = wp_cache_get( $cache_key, 'terms' ); |
680 | | if ( false !== $cache ) { |
681 | | if ( 'all' === $_fields ) { |
682 | | $cache = array_map( 'get_term', $cache ); |
683 | | } |
684 | 665 | |
685 | | $this->terms = $cache; |
686 | | return $this->terms; |
687 | | } |
| 666 | if ( 'count' === $_fields ) { |
| 667 | if ( false === $cache ) { |
| 668 | $cache = $wpdb->get_var( $this->request ); |
| 669 | wp_cache_set( $cache_key, $cache, 'terms' ); |
| 670 | } |
688 | 671 | |
689 | | if ( 'count' == $_fields ) { |
690 | | $count = $wpdb->get_var( $this->request ); |
691 | | wp_cache_set( $cache_key, $count, 'terms' ); |
692 | | return $count; |
| 672 | return $cache; |
693 | 673 | } |
694 | 674 | |
695 | | $terms = $wpdb->get_results( $this->request ); |
696 | | if ( 'all' == $_fields || 'all_with_object_id' === $_fields ) { |
697 | | update_term_cache( $terms ); |
| 675 | if ( false === $cache ) { |
| 676 | $results = $wpdb->get_results( $this->request ); |
| 677 | $cache = wp_list_pluck( $results, 'term_id' ); |
| 678 | wp_cache_set( $cache_key, $cache, 'terms' ); |
698 | 679 | } |
699 | 680 | |
700 | | // Prime termmeta cache. |
701 | | if ( $args['update_term_meta_cache'] ) { |
702 | | $term_ids = wp_list_pluck( $terms, 'term_id' ); |
703 | | update_termmeta_cache( $term_ids ); |
704 | | } |
| 681 | $term_ids = $cache; |
705 | 682 | |
706 | | if ( empty( $terms ) ) { |
| 683 | if ( empty( $term_ids ) ) { |
707 | 684 | wp_cache_add( $cache_key, array(), 'terms', DAY_IN_SECONDS ); |
708 | 685 | return array(); |
709 | 686 | } |
710 | 687 | |
| 688 | // 'ids' requests only need full term objects in certain cases. |
| 689 | if ( 'ids' === $_fields && ! $child_of && ( ! $hierarchical || ! $args['hide_empty'] ) ) { |
| 690 | $terms = array(); |
| 691 | foreach ( $term_ids as $term_id ) { |
| 692 | $_term = new stdClass; |
| 693 | $_term->term_id = $term_id; |
| 694 | $terms[] = $_term; |
| 695 | } |
| 696 | |
| 697 | if ( $args['update_term_meta_cache'] ) { |
| 698 | update_termmeta_cache( $term_ids ); |
| 699 | } |
| 700 | } else { |
| 701 | _prime_term_caches( $term_ids, $args['update_term_meta_cache'] ); |
| 702 | $terms = array_map( 'get_term', $term_ids ); |
| 703 | |
| 704 | if ( in_array( 'tr.object_id', $selects ) ) { |
| 705 | foreach ( $results as $index => $result ) { |
| 706 | $terms[ $index ]->object_id = (int) $result->object_id; |
| 707 | } |
| 708 | } |
| 709 | } |
| 710 | |
711 | 711 | if ( $child_of ) { |
712 | 712 | foreach ( $taxonomies as $_tax ) { |
713 | 713 | $children = _get_term_hierarchy( $_tax ); |
… |
… |
class WP_Term_Query { |
758 | 758 | } |
759 | 759 | |
760 | 760 | $_tt_ids[ $term->term_id ] = 1; |
761 | | $_terms[] = $term; |
| 761 | $_terms[] = $term; |
762 | 762 | } |
763 | 763 | |
764 | 764 | $terms = $_terms; |
… |
… |
class WP_Term_Query { |
808 | 808 | } |
809 | 809 | } |
810 | 810 | |
811 | | wp_cache_add( $cache_key, $terms, 'terms', DAY_IN_SECONDS ); |
812 | | |
813 | | if ( 'all' === $_fields || 'all_with_object_id' === $_fields ) { |
814 | | $terms = array_map( 'get_term', $terms ); |
815 | | } |
816 | | |
817 | 811 | $this->terms = $terms; |
| 812 | |
818 | 813 | return $this->terms; |
819 | 814 | } |
820 | 815 | |
diff --git src/wp-includes/class-wp-term.php src/wp-includes/class-wp-term.php
index 8eb87efbe0..63381b6db0 100644
|
|
final class WP_Term { |
140 | 140 | return false; |
141 | 141 | } |
142 | 142 | |
| 143 | $_term = false; |
| 144 | |
143 | 145 | // If a taxonomy was specified, find a match. |
144 | 146 | if ( $taxonomy ) { |
145 | 147 | foreach ( $terms as $match ) { |
diff --git src/wp-includes/taxonomy.php src/wp-includes/taxonomy.php
index 0c4012242c..e8c3e149f4 100644
|
|
function clean_term_cache($ids, $taxonomy = '', $clean_taxonomy = true) { |
3003 | 3003 | |
3004 | 3004 | foreach ( $taxonomies as $taxonomy ) { |
3005 | 3005 | if ( $clean_taxonomy ) { |
3006 | | wp_cache_delete('all_ids', $taxonomy); |
3007 | | wp_cache_delete('get', $taxonomy); |
3008 | | delete_option("{$taxonomy}_children"); |
3009 | | // Regenerate {$taxonomy}_children |
3010 | | _get_term_hierarchy($taxonomy); |
| 3006 | clean_taxonomy_cache( $taxonomy ); |
3011 | 3007 | } |
3012 | 3008 | |
3013 | 3009 | /** |
… |
… |
function clean_term_cache($ids, $taxonomy = '', $clean_taxonomy = true) { |
3027 | 3023 | } |
3028 | 3024 | |
3029 | 3025 | /** |
| 3026 | * Clean the caches for a taxonomy. |
| 3027 | * |
| 3028 | * @since 4.9.0 |
| 3029 | * |
| 3030 | * @param string $taxonomy Taxonomy slug. |
| 3031 | */ |
| 3032 | function clean_taxonomy_cache( $taxonomy ) { |
| 3033 | wp_cache_delete( 'all_ids', $taxonomy ); |
| 3034 | wp_cache_delete( 'get', $taxonomy ); |
| 3035 | |
| 3036 | // Regenerate cached hierarchy. |
| 3037 | delete_option( "{$taxonomy}_children" ); |
| 3038 | _get_term_hierarchy( $taxonomy ); |
| 3039 | |
| 3040 | /** |
| 3041 | * Fires after a taxonomy's caches have been cleaned. |
| 3042 | * |
| 3043 | * @since 4.9.0 |
| 3044 | * |
| 3045 | * @param string $taxonomy Taxonomy slug. |
| 3046 | */ |
| 3047 | do_action( 'clean_term_cache', $taxonomy ); |
| 3048 | } |
| 3049 | |
| 3050 | /** |
3030 | 3051 | * Retrieves the taxonomy relationship to the term object id. |
3031 | 3052 | * |
3032 | 3053 | * Upstream functions (like get_the_terms() and is_object_in_term()) are |
… |
… |
function _split_shared_term( $term_id, $term_taxonomy_id, $record = true ) { |
3527 | 3548 | array( 'parent' => $new_term_id ), |
3528 | 3549 | array( 'term_taxonomy_id' => $child_tt_id ) |
3529 | 3550 | ); |
3530 | | clean_term_cache( $term_id, $term_taxonomy->taxonomy ); |
| 3551 | clean_term_cache( (int) $child_tt_id, '', false ); |
3531 | 3552 | } |
3532 | 3553 | } else { |
3533 | 3554 | // If the term has no children, we must force its taxonomy cache to be rebuilt separately. |
3534 | | clean_term_cache( $new_term_id, $term_taxonomy->taxonomy ); |
| 3555 | clean_term_cache( $new_term_id, $term_taxonomy->taxonomy, false ); |
3535 | 3556 | } |
3536 | 3557 | |
| 3558 | clean_term_cache( $term_id, $term_taxonomy->taxonomy, false ); |
| 3559 | |
| 3560 | /* |
| 3561 | * Taxonomy cache clearing is delayed to avoid race conditions that may occur when |
| 3562 | * regenerating the taxonomy's hierarchy tree. |
| 3563 | */ |
| 3564 | $taxonomies_to_clean = array( $term_taxonomy->taxonomy ); |
| 3565 | |
3537 | 3566 | // Clean the cache for term taxonomies formerly shared with the current term. |
3538 | | $shared_term_taxonomies = $wpdb->get_row( $wpdb->prepare( "SELECT taxonomy FROM $wpdb->term_taxonomy WHERE term_id = %d", $term_id ) ); |
3539 | | if ( $shared_term_taxonomies ) { |
3540 | | foreach ( $shared_term_taxonomies as $shared_term_taxonomy ) { |
3541 | | clean_term_cache( $term_id, $shared_term_taxonomy ); |
3542 | | } |
| 3567 | $shared_term_taxonomies = $wpdb->get_col( $wpdb->prepare( "SELECT taxonomy FROM $wpdb->term_taxonomy WHERE term_id = %d", $term_id ) ); |
| 3568 | $taxonomies_to_clean = array_merge( $taxonomies_to_clean, $shared_term_taxonomies ); |
| 3569 | |
| 3570 | foreach ( $taxonomies_to_clean as $taxonomy_to_clean ) { |
| 3571 | clean_taxonomy_cache( $taxonomy_to_clean ); |
3543 | 3572 | } |
3544 | 3573 | |
3545 | 3574 | // Keep a record of term_ids that have been split, keyed by old term_id. See wp_get_split_term(). |
diff --git tests/phpunit/tests/term/cache.php tests/phpunit/tests/term/cache.php
index fb7a5a84ab..b303bc840a 100644
|
|
class Tests_Term_Cache extends WP_UnitTestCase { |
229 | 229 | $term_id = $this->factory->term->create( array( 'slug' => 'burrito', 'name' => 'Taco', 'taxonomy' => 'post_tag' ) ); |
230 | 230 | |
231 | 231 | clean_term_cache( $term_id, 'post_tag' ); |
232 | | $num_queries = $wpdb->num_queries; |
233 | 232 | |
234 | 233 | $term = get_term_by( 'slug', 'burrito', 'post_tag' ); |
235 | | $num_queries++; |
236 | 234 | $this->assertEquals( 'Taco', $term->name ); |
237 | | $this->assertEquals( $num_queries, $wpdb->num_queries ); |
238 | 235 | |
239 | 236 | // This should now hit cache. |
| 237 | $num_queries = $wpdb->num_queries; |
240 | 238 | $term = get_term_by( 'slug', 'burrito', 'post_tag' ); |
241 | 239 | $this->assertEquals( 'Taco', $term->name ); |
242 | 240 | $this->assertEquals( $num_queries, $wpdb->num_queries ); |
… |
… |
class Tests_Term_Cache extends WP_UnitTestCase { |
254 | 252 | $term_id = $this->factory->term->create( array( 'slug' => 'burrito', 'name' => 'Taco', 'taxonomy' => 'post_tag' ) ); |
255 | 253 | |
256 | 254 | clean_term_cache( $term_id, 'post_tag' ); |
257 | | $num_queries = $wpdb->num_queries; |
258 | 255 | |
259 | 256 | $term = get_term_by( 'slug', 'burrito', 'post_tag' ); |
260 | | $num_queries++; |
261 | 257 | $this->assertEquals( 'Taco', $term->name ); |
262 | | $this->assertEquals( $num_queries, $wpdb->num_queries ); |
263 | 258 | |
264 | 259 | // This should now hit cache. |
| 260 | $num_queries = $wpdb->num_queries; |
265 | 261 | $term = get_term_by( 'slug', 'burrito', 'post_tag' ); |
266 | 262 | $this->assertEquals( 'Taco', $term->name ); |
267 | 263 | $this->assertEquals( $num_queries, $wpdb->num_queries ); |
… |
… |
class Tests_Term_Cache extends WP_UnitTestCase { |
272 | 268 | |
273 | 269 | // This should not hit cache. |
274 | 270 | $term = get_term_by( 'slug', 'burrito', 'post_tag' ); |
275 | | $num_queries++; |
276 | 271 | $this->assertEquals( 'No Taco', $term->name ); |
277 | | $this->assertEquals( $num_queries, $wpdb->num_queries ); |
| 272 | $this->assertGreaterThan( $num_queries, $wpdb->num_queries ); |
278 | 273 | } |
279 | 274 | |
280 | 275 | /** |
… |
… |
class Tests_Term_Cache extends WP_UnitTestCase { |
286 | 281 | $term_id = $this->factory->term->create( array( 'name' => 'Burrito', 'slug' => 'noburrito', 'taxonomy' => 'post_tag' ) ); |
287 | 282 | |
288 | 283 | clean_term_cache( $term_id, 'post_tag' ); |
289 | | $num_queries = $wpdb->num_queries; |
290 | 284 | |
291 | | get_term_by( 'name', 'Burrito', 'post_tag' ); |
292 | | $num_queries++; |
293 | | $this->assertEquals( $num_queries, $wpdb->num_queries ); |
| 285 | $term = get_term_by( 'name', 'Burrito', 'post_tag' ); |
| 286 | $this->assertSame( 'Burrito', $term->name ); |
294 | 287 | |
295 | 288 | // This should now hit cache. |
| 289 | $num_queries = $wpdb->num_queries; |
296 | 290 | $term = get_term_by( 'name', 'Burrito', 'post_tag' ); |
297 | 291 | $this->assertEquals( $num_queries, $wpdb->num_queries ); |
298 | 292 | |
… |
… |
class Tests_Term_Cache extends WP_UnitTestCase { |
309 | 303 | $term_id = $this->factory->term->create( array( 'name' => 'Burrito', 'slug' => 'noburrito', 'taxonomy' => 'post_tag' ) ); |
310 | 304 | |
311 | 305 | clean_term_cache( $term_id, 'post_tag' ); |
312 | | $num_queries = $wpdb->num_queries; |
313 | 306 | |
314 | 307 | get_term_by( 'name', 'Burrito', 'post_tag' ); |
315 | | $num_queries++; |
316 | | $this->assertEquals( $num_queries, $wpdb->num_queries ); |
317 | 308 | |
318 | 309 | // This should now hit cache. |
| 310 | $num_queries = $wpdb->num_queries; |
319 | 311 | get_term_by( 'name', 'Burrito', 'post_tag' ); |
320 | 312 | $this->assertEquals( $num_queries, $wpdb->num_queries ); |
321 | 313 | |
… |
… |
class Tests_Term_Cache extends WP_UnitTestCase { |
325 | 317 | |
326 | 318 | // This should not hit cache. |
327 | 319 | get_term_by( 'name', 'burrito', 'post_tag' ); |
328 | | $num_queries++; |
329 | | $this->assertEquals( $num_queries, $wpdb->num_queries ); |
| 320 | $this->assertGreaterThan( $num_queries, $wpdb->num_queries ); |
330 | 321 | } |
331 | 322 | |
332 | 323 | /** |
… |
… |
class Tests_Term_Cache extends WP_UnitTestCase { |
338 | 329 | $term_id = $this->factory->term->create( array( 'name' => 'Burrito', 'taxonomy' => 'post_tag' ) ); |
339 | 330 | |
340 | 331 | clean_term_cache( $term_id, 'post_tag' ); |
341 | | $num_queries = $wpdb->num_queries; |
342 | 332 | $last_changed = wp_cache_get( 'last_changed', 'terms' ); |
343 | 333 | |
344 | 334 | $term1 = get_term_by( 'name', 'Burrito', 'post_tag' ); |
345 | | $num_queries++; |
346 | 335 | |
347 | 336 | // Verify the term is cached. |
| 337 | $num_queries = $wpdb->num_queries; |
348 | 338 | $term2 = get_term_by( 'name', 'Burrito', 'post_tag' ); |
349 | 339 | $this->assertEquals( $num_queries, $wpdb->num_queries ); |
350 | 340 | $this->assertEquals( $term1, $term2 ); |
… |
… |
class Tests_Term_Cache extends WP_UnitTestCase { |
377 | 367 | add_term_meta( $term_id, 'foo', 'bar' ); |
378 | 368 | |
379 | 369 | clean_term_cache( $term_id, 'post_tag' ); |
380 | | $num_queries = $wpdb->num_queries; |
381 | 370 | |
382 | 371 | $term = get_term_by( 'name', 'Burrito', 'post_tag' ); |
383 | | $num_queries++; |
384 | 372 | $this->assertTrue( $term instanceof WP_Term ); |
385 | 373 | $this->assertSame( $term_id, $term->term_id ); |
386 | | $this->assertEquals( $num_queries, $wpdb->num_queries ); |
387 | 374 | |
| 375 | $num_queries = $wpdb->num_queries; |
388 | 376 | $term_meta = get_term_meta( $term_id, 'foo', true ); |
389 | 377 | $num_queries++; |
390 | 378 | $this->assertSame( $term_meta, 'bar' ); |
diff --git tests/phpunit/tests/term/getTermBy.php tests/phpunit/tests/term/getTermBy.php
index 4adda74f5a..ea8c386779 100644
|
|
class Tests_Term_GetTermBy extends WP_UnitTestCase { |
108 | 108 | |
109 | 109 | clean_term_cache( $t, 'wptests_tax' ); |
110 | 110 | |
111 | | $num_queries = $wpdb->num_queries; |
112 | 111 | $found = get_term_by( 'slug', 'foo', 'wptests_tax' ); |
113 | | $num_queries++; |
114 | 112 | |
115 | 113 | $this->assertTrue( $found instanceof WP_Term ); |
116 | 114 | $this->assertSame( $t, $found->term_id ); |
117 | | $this->assertSame( $num_queries, $wpdb->num_queries ); |
118 | 115 | |
119 | 116 | // Calls to `get_term()` should now hit cache. |
| 117 | $num_queries = $wpdb->num_queries; |
120 | 118 | $found2 = get_term( $t ); |
121 | 119 | $this->assertSame( $t, $found->term_id ); |
122 | 120 | $this->assertSame( $num_queries, $wpdb->num_queries ); |
… |
… |
class Tests_Term_GetTermBy extends WP_UnitTestCase { |
172 | 170 | /** |
173 | 171 | * @ticket 21760 |
174 | 172 | */ |
175 | | public function test_query_should_not_contain_order_by_clause() { |
176 | | global $wpdb; |
177 | | |
178 | | $term_id = $this->factory->term->create( array( 'name' => 'burrito', 'taxonomy' => 'post_tag' ) ); |
179 | | $found = get_term_by( 'name', 'burrito', 'post_tag' ); |
180 | | $this->assertSame( $term_id, $found->term_id ); |
181 | | $this->assertNotContains( 'ORDER BY', $wpdb->last_query ); |
182 | | } |
183 | | |
184 | | /** |
185 | | * @ticket 21760 |
186 | | */ |
187 | | public function test_query_should_contain_limit_clause() { |
188 | | global $wpdb; |
189 | | |
190 | | $term_id = $this->factory->term->create( array( 'name' => 'burrito', 'taxonomy' => 'post_tag' ) ); |
191 | | $found = get_term_by( 'name', 'burrito', 'post_tag' ); |
192 | | $this->assertSame( $term_id, $found->term_id ); |
193 | | $this->assertContains( 'LIMIT 1', $wpdb->last_query ); |
194 | | } |
195 | | |
196 | | /** |
197 | | * @ticket 21760 |
198 | | */ |
199 | 173 | public function test_prevent_recursion_by_get_terms_filter() { |
200 | 174 | $action = new MockAction(); |
201 | 175 | |
diff --git tests/phpunit/tests/term/getTerms.php tests/phpunit/tests/term/getTerms.php
index 575e5ca1c0..01a79faddd 100644
|
|
class Tests_Term_getTerms extends WP_UnitTestCase { |
112 | 112 | $this->assertEquals( 3, count( $terms ) ); |
113 | 113 | $time1 = wp_cache_get( 'last_changed', 'terms' ); |
114 | 114 | $this->assertNotEmpty( $time1 ); |
115 | | $this->assertEquals( $num_queries + 1, $wpdb->num_queries ); |
| 115 | $this->assertEquals( $num_queries + 2, $wpdb->num_queries ); |
116 | 116 | |
117 | 117 | $num_queries = $wpdb->num_queries; |
118 | 118 | |
diff --git tests/phpunit/tests/term/isObjectInTerm.php tests/phpunit/tests/term/isObjectInTerm.php
index ed054f8ea3..5aa55d88a0 100644
|
|
class Tests_IsObjectInTerm extends WP_UnitTestCase { |
113 | 113 | $o = 12345; |
114 | 114 | wp_set_object_terms( $o, $terms[0], 'wptests_tax' ); |
115 | 115 | |
116 | | $num_queries = $wpdb->num_queries; |
117 | 116 | $this->assertTrue( is_object_in_term( $o, 'wptests_tax', $terms[0] ) ); |
118 | | $num_queries++; |
119 | | $this->assertSame( $num_queries, $wpdb->num_queries ); |
120 | 117 | |
| 118 | $num_queries = $wpdb->num_queries; |
121 | 119 | $this->assertFalse( is_object_in_term( $o, 'wptests_tax', $terms[1] ) ); |
122 | 120 | $this->assertSame( $num_queries, $wpdb->num_queries ); |
123 | 121 | } |
… |
… |
class Tests_IsObjectInTerm extends WP_UnitTestCase { |
134 | 132 | $o = 12345; |
135 | 133 | wp_set_object_terms( $o, $terms[0], 'wptests_tax' ); |
136 | 134 | |
137 | | $num_queries = $wpdb->num_queries; |
138 | 135 | $this->assertTrue( is_object_in_term( $o, 'wptests_tax', $terms[0] ) ); |
139 | | $num_queries++; |
140 | | $this->assertSame( $num_queries, $wpdb->num_queries ); |
141 | 136 | |
142 | 137 | wp_set_object_terms( $o, $terms[1], 'wptests_tax' ); |
143 | 138 | |
144 | | $num_queries = $wpdb->num_queries; |
145 | 139 | $this->assertTrue( is_object_in_term( $o, 'wptests_tax', $terms[1] ) ); |
146 | | $num_queries++; |
147 | | $this->assertSame( $num_queries, $wpdb->num_queries ); |
148 | 140 | } |
149 | 141 | |
150 | 142 | /** |