Ticket #10142: 10142.diff
File 10142.diff, 20.0 KB (added by , 8 years ago) |
---|
-
src/wp-admin/includes/schema.php
diff --git src/wp-admin/includes/schema.php src/wp-admin/includes/schema.php index dd36834..d809ec5 100644
function wp_get_db_schema( $scope = 'all', $blog_id = null ) { 57 57 $max_index_length = 191; 58 58 59 59 // Blog specific tables. 60 $blog_tables = "CREATE TABLE $wpdb->terms ( 60 $blog_tables = "CREATE TABLE $wpdb->termmeta ( 61 meta_id bigint(20) unsigned NOT NULL auto_increment, 62 term_id bigint(20) unsigned NOT NULL default '0', 63 meta_key varchar(255) default NULL, 64 meta_value longtext, 65 PRIMARY KEY (meta_id), 66 KEY term_id (term_id), 67 KEY meta_key (meta_key($max_index_length)) 68 ) $charset_collate; 69 CREATE TABLE $wpdb->terms ( 61 70 term_id bigint(20) unsigned NOT NULL auto_increment, 62 71 name varchar(200) NOT NULL default '', 63 72 slug varchar(200) NOT NULL default '', -
src/wp-includes/default-filters.php
diff --git src/wp-includes/default-filters.php src/wp-includes/default-filters.php index e9ddc47..20684e2 100644
add_filter( 'pingback_ping_source_uri', 'pingback_ping_source_uri' ); 201 201 add_filter( 'xmlrpc_pingback_error', 'xmlrpc_pingback_error' ); 202 202 add_filter( 'title_save_pre', 'trim' ); 203 203 add_filter( 'get_comment_metadata', 'wp_lazyload_comment_meta', 10, 2 ); 204 add_filter( 'get_term_metadata', 'wp_lazyload_term_meta', 10, 2 ); 204 205 205 206 add_filter( 'http_request_host_is_external', 'allowed_http_request_hosts', 10, 2 ); 206 207 -
src/wp-includes/taxonomy-functions.php
diff --git src/wp-includes/taxonomy-functions.php src/wp-includes/taxonomy-functions.php index 280f440..74dd8a5 100644
function get_term_to_edit( $id, $taxonomy ) { 1003 1003 * no effect on non-hierarchical taxonomies. Default false. 1004 1004 * @type string $cache_domain Unique cache key to be produced when this query is stored in an 1005 1005 * object cache. Default is 'core'. 1006 * @type bool $update_term_meta_cache Whether to prime meta caches for matched terms. Default true. 1006 1007 * } 1007 1008 * @return array|int|WP_Error List of Term Objects and their children. Will return WP_Error, if any of $taxonomies 1008 1009 * do not exist. … … function get_terms( $taxonomies, $args = '' ) { 1026 1027 'hide_empty' => true, 'exclude' => array(), 'exclude_tree' => array(), 'include' => array(), 1027 1028 'number' => '', 'fields' => 'all', 'name' => '', 'slug' => '', 'parent' => '', 'childless' => false, 1028 1029 'hierarchical' => true, 'child_of' => 0, 'get' => '', 'name__like' => '', 'description__like' => '', 1029 'pad_counts' => false, 'offset' => '', 'search' => '', 'cache_domain' => 'core' ); 1030 'pad_counts' => false, 'offset' => '', 'search' => '', 'cache_domain' => 'core', 1031 'update_term_meta_cache' => true ); 1030 1032 $args = wp_parse_args( $args, $defaults ); 1031 1033 $args['number'] = absint( $args['number'] ); 1032 1034 $args['offset'] = absint( $args['offset'] ); … … function get_terms( $taxonomies, $args = '' ) { 1364 1366 update_term_cache( $terms ); 1365 1367 } 1366 1368 1369 // Prime termmeta cache. 1370 if ( $args['update_term_meta_cache'] ) { 1371 $term_ids = wp_list_pluck( $terms, 'term_id' ); 1372 update_termmeta_cache( $term_ids ); 1373 } 1374 1367 1375 if ( empty($terms) ) { 1368 1376 wp_cache_add( $cache_key, array(), 'terms', DAY_IN_SECONDS ); 1369 1377 … … function get_terms( $taxonomies, $args = '' ) { 1445 1453 } 1446 1454 1447 1455 /** 1456 * Add meta data field to a comment. 1457 * 1458 * @since 4.4.0 1459 * 1460 * @param int $term_id Term ID. 1461 * @param string $meta_key Metadata name. 1462 * @param mixed $meta_value Metadata value. 1463 * @param bool $unique Optional, default is false. Whether the same key should not be added. 1464 * @return int|bool Meta ID on success, false on failure. 1465 */ 1466 function add_term_meta( $term_id, $meta_key, $meta_value, $unique = false ) { 1467 return add_metadata( 'term', $term_id, $meta_key, $meta_value, $unique ); 1468 } 1469 1470 /** 1471 * Remove metadata matching criteria from a term. 1472 * 1473 * You can match based on the key, or key and value. Removing based on key and 1474 * value, will keep from removing duplicate metadata with the same key. It also 1475 * allows removing all metadata matching key, if needed. 1476 * 1477 * @since 4.4.0 1478 * 1479 * @param int $term_id Term ID. 1480 * @param string $meta_key Metadata name. 1481 * @param mixed $meta_value Optional. Metadata value. 1482 * @return bool True on success, false on failure. 1483 */ 1484 function delete_term_meta( $term_id, $meta_key, $meta_value = '' ) { 1485 return delete_metadata( 'term', $term_id, $meta_key, $meta_value ); 1486 } 1487 1488 /** 1489 * Retrieve meta field for a term. 1490 * 1491 * @since 4.4.0 1492 * 1493 * @param int $term_id Term ID. 1494 * @param string $key Optional. The meta key to retrieve. By default, returns data for all keys. 1495 * @param bool $single Whether to return a single value. 1496 * @return mixed Will be an array if $single is false. Will be value of meta data field if $single 1497 * is true. 1498 */ 1499 function get_term_meta( $term_id, $key = '', $single = false ) { 1500 return get_metadata( 'term', $term_id, $key, $single ); 1501 } 1502 1503 /** 1504 * Update term meta field based on term ID. 1505 * 1506 * Use the $prev_value parameter to differentiate between meta fields with the 1507 * same key and term ID. 1508 * 1509 * If the meta field for the term does not exist, it will be added. 1510 * 1511 * @since 4.4.0 1512 * 1513 * @param int $term_id Term ID. 1514 * @param string $meta_key Metadata key. 1515 * @param mixed $meta_value Metadata value. 1516 * @param mixed $prev_value Optional. Previous value to check before removing. 1517 * @return int|bool Meta ID if the key didn't exist, true on successful update, false on failure. 1518 */ 1519 function update_term_meta( $term_id, $meta_key, $meta_value, $prev_value = '' ) { 1520 return update_metadata( 'term', $term_id, $meta_key, $meta_value, $prev_value ); 1521 } 1522 1523 /** 1524 * Updates metadata cache for list of term IDs. 1525 * 1526 * Performs SQL query to retrieve the metadata for the term IDs and updates the 1527 * metadata cache for the terms. Therefore, the functions, which call this 1528 * function, do not need to perform SQL queries on their own. 1529 * 1530 * @since 4.4.0 1531 * 1532 * @param array $term_ids List of post IDs. 1533 * @return array|false Returns false if there is nothing to update. Returns an array of metadata on success. 1534 */ 1535 function update_termmeta_cache( $term_ids ) { 1536 return update_meta_cache( 'term', $term_ids ); 1537 } 1538 1539 /** 1540 * Lazy load termmeta when inside of a `WP_Query` loop. 1541 * 1542 * @since 4.4.0 1543 * 1544 * @param null $check The `$check` param passed from the 'pre_term_metadata' hook. 1545 * @param int $term_id ID of the term whose metadata is being cached. 1546 * @return null In order not to short-circuit `get_metadata()`. 1547 */ 1548 function wp_lazyload_term_meta( $check, $term_id ) { 1549 global $wp_query; 1550 1551 if ( $wp_query instanceof WP_Query && ! empty( $wp_query->posts ) && ! empty( $wp_query->get( 'update_post_term_cache' ) ) ) { 1552 // We can only lazyload if the entire post object is present. 1553 $posts = array(); 1554 foreach ( $wp_query->posts as $post ) { 1555 if ( $post instanceof WP_Post ) { 1556 $posts[] = $post; 1557 } 1558 } 1559 1560 if ( empty( $posts ) ) { 1561 return; 1562 } 1563 1564 // Fetch cached term_ids for each post. Keyed by term_id for faster lookup. 1565 $term_ids = array(); 1566 foreach ( $posts as $post ) { 1567 $taxonomies = get_object_taxonomies( $post->post_type ); 1568 foreach ( $taxonomies as $taxonomy ) { 1569 $terms = get_object_term_cache( $post->ID, $taxonomy ); 1570 if ( false !== $terms ) { 1571 foreach ( $terms as $term ) { 1572 if ( ! isset( $term_ids[ $term->term_id ] ) ) { 1573 $term_ids[ $term->term_id ] = 1; 1574 } 1575 } 1576 } 1577 } 1578 } 1579 1580 if ( $term_ids ) { 1581 update_termmeta_cache( array_keys( $term_ids ) ); 1582 } 1583 } 1584 1585 return $check; 1586 } 1587 1588 /** 1448 1589 * Check if Term exists. 1449 1590 * 1450 1591 * Formerly is_term(), introduced in 2.3.0. … … function wp_delete_category( $cat_ID ) { 2016 2157 * term objects being returned, 'ids' will return an array of integers, and 'names' an array 2017 2158 * of strings. 2018 2159 * @type int $parent Optional. Limit results to the direct children of a given term ID. 2160 * @type bool $update_term_meta_cache Whether to prime termmeta cache for matched terms. Only applies when 2161 * `$fields` is 'all', 'all_with_object_id', or 'term_id'. Default: true. 2019 2162 * } 2020 2163 * @return array|WP_Error The requested term data or empty array if no terms found. 2021 2164 * WP_Error if any of the $taxonomies don't exist. … … function wp_get_object_terms($object_ids, $taxonomies, $args = array()) { 2043 2186 'order' => 'ASC', 2044 2187 'fields' => 'all', 2045 2188 'parent' => '', 2189 'update_term_meta_cache' => true, 2046 2190 ); 2047 2191 $args = wp_parse_args( $args, $defaults ); 2048 2192 … … function wp_get_object_terms($object_ids, $taxonomies, $args = array()) { 2143 2287 } 2144 2288 } 2145 2289 2290 // Update termmeta cache, if necessary. 2291 if ( $args['update_term_meta_cache'] && ( 'all' === $fields || 'all_with_object_ids' === $fields || 'term_id' === $fields ) ) { 2292 if ( 'term_id' === $fields ) { 2293 $term_ids = $fields; 2294 } else { 2295 $term_ids = wp_list_pluck( $terms, 'term_id' ); 2296 } 2297 2298 update_termmeta_cache( $term_ids ); 2299 } 2300 2146 2301 if ( ! $terms ) { 2147 2302 $terms = array(); 2148 2303 } elseif ( $objects && 'all_with_object_id' !== $fields ) { … … function update_object_term_cache($object_ids, $object_type) { 3278 3433 $terms = wp_get_object_terms( $ids, $taxonomies, array( 3279 3434 'fields' => 'all_with_object_id', 3280 3435 'orderby' => 'none', 3436 'update_term_meta_cache' => false, 3281 3437 ) ); 3282 3438 3283 3439 $object_terms = array(); -
src/wp-includes/wp-db.php
diff --git src/wp-includes/wp-db.php src/wp-includes/wp-db.php index a0c59cf..6ee7eca 100644
class wpdb { 266 266 * @var array 267 267 */ 268 268 var $tables = array( 'posts', 'comments', 'links', 'options', 'postmeta', 269 'terms', 'term_taxonomy', 'term_relationships', ' commentmeta' );269 'terms', 'term_taxonomy', 'term_relationships', 'termmeta', 'commentmeta' ); 270 270 271 271 /** 272 272 * List of deprecated WordPress tables … … class wpdb { 382 382 */ 383 383 public $term_taxonomy; 384 384 385 /** 386 * WordPress Term Meta table. 387 * 388 * @since 4.4.0 389 * @access public 390 * @var string 391 */ 392 public $termmeta; 393 385 394 /* 386 395 * Global and Multisite tables 387 396 */ -
tests/phpunit/tests/term/getTerms.php
diff --git tests/phpunit/tests/term/getTerms.php tests/phpunit/tests/term/getTerms.php index b170648..8eb40e2 100644
class Tests_Term_getTerms extends WP_UnitTestCase { 22 22 $num_queries = $wpdb->num_queries; 23 23 24 24 // last_changed and num_queries should bump 25 $terms = get_terms( 'post_tag' );25 $terms = get_terms( 'post_tag', array( 'update_term_meta_cache' => false ) ); 26 26 $this->assertEquals( 3, count( $terms ) ); 27 27 $time1 = wp_cache_get( 'last_changed', 'terms' ); 28 28 $this->assertNotEmpty( $time1 ); … … class Tests_Term_getTerms extends WP_UnitTestCase { 31 31 $num_queries = $wpdb->num_queries; 32 32 33 33 // Again. last_changed and num_queries should remain the same. 34 $terms = get_terms( 'post_tag' );34 $terms = get_terms( 'post_tag', array( 'update_term_meta_cache' => false ) ); 35 35 $this->assertEquals( 3, count( $terms ) ); 36 36 $this->assertEquals( $time1, wp_cache_get( 'last_changed', 'terms' ) ); 37 37 $this->assertEquals( $num_queries, $wpdb->num_queries ); … … class Tests_Term_getTerms extends WP_UnitTestCase { 1502 1502 } 1503 1503 } 1504 1504 1505 /** 1506 * @ticket 10142 1507 */ 1508 public function test_termmeta_cache_should_be_primed_by_default() { 1509 global $wpdb; 1510 1511 register_taxonomy( 'wptests_tax', 'post' ); 1512 $terms = $this->factory->term->create_many( 3, array( 'taxonomy' => 'wptests_tax' ) ); 1513 add_term_meta( $terms[0], 'foo', 'bar' ); 1514 add_term_meta( $terms[1], 'foo', 'bar' ); 1515 add_term_meta( $terms[2], 'foo', 'bar' ); 1516 1517 $found = get_terms( 'wptests_tax', array( 1518 'hide_empty' => false, 1519 'include' => $terms, 1520 ) ); 1521 1522 $num_queries = $wpdb->num_queries; 1523 1524 foreach ( $terms as $t ) { 1525 $this->assertSame( 'bar', get_term_meta( $t, 'foo', true ) ); 1526 } 1527 1528 $this->assertSame( $num_queries, $wpdb->num_queries ); 1529 } 1530 1531 /** 1532 * @ticket 10142 1533 */ 1534 public function test_termmeta_cache_should_not_be_primed_when_update_term_meta_cache_is_false() { 1535 global $wpdb; 1536 1537 register_taxonomy( 'wptests_tax', 'post' ); 1538 $terms = $this->factory->term->create_many( 3, array( 'taxonomy' => 'wptests_tax' ) ); 1539 add_term_meta( $terms[0], 'foo', 'bar' ); 1540 add_term_meta( $terms[1], 'foo', 'bar' ); 1541 add_term_meta( $terms[2], 'foo', 'bar' ); 1542 1543 $found = get_terms( 'wptests_tax', array( 1544 'hide_empty' => false, 1545 'include' => $terms, 1546 'update_term_meta_cache' => false, 1547 ) ); 1548 1549 $num_queries = $wpdb->num_queries; 1550 1551 foreach ( $terms as $t ) { 1552 $this->assertSame( 'bar', get_term_meta( $t, 'foo', true ) ); 1553 } 1554 1555 $this->assertSame( $num_queries + 3, $wpdb->num_queries ); 1556 } 1557 1505 1558 protected function create_hierarchical_terms_and_posts() { 1506 1559 $terms = array(); 1507 1560 -
new file tests/phpunit/tests/term/meta.php
diff --git tests/phpunit/tests/term/meta.php tests/phpunit/tests/term/meta.php new file mode 100644 index 0000000..bdd4b36
- + 1 <?php 2 3 /** 4 * @group taxonomy 5 * @group meta 6 * @ticket 10142 7 */ 8 class Tests_Term_Meta extends WP_UnitTestCase { 9 public function setUp() { 10 parent::setUp(); 11 register_taxonomy( 'wptests_tax', 'post' ); 12 } 13 14 public function test_add() { 15 $t = $this->factory->term->create( array( 'taxonomy' => 'wptests_tax' ) ); 16 17 $this->assertNotEmpty( add_term_meta( $t, 'foo', 'bar' ) ); 18 } 19 20 public function test_add_unique() { 21 $t = $this->factory->term->create( array( 'taxonomy' => 'wptests_tax' ) ); 22 23 $this->assertNotEmpty( add_term_meta( $t, 'foo', 'bar' ) ); 24 $this->assertFalse( add_term_meta( $t, 'foo', 'bar', true ) ); 25 } 26 27 public function test_delete() { 28 $t = $this->factory->term->create( array( 'taxonomy' => 'wptests_tax' ) ); 29 add_term_meta( $t, 'foo', 'bar' ); 30 31 $this->assertTrue( delete_term_meta( $t, 'foo' ) ); 32 } 33 34 public function test_delete_with_invalid_meta_key_should_return_false() { 35 $t = $this->factory->term->create( array( 'taxonomy' => 'wptests_tax' ) ); 36 37 $this->assertFalse( delete_term_meta( $t, 'foo' ) ); 38 } 39 40 public function test_delete_should_respect_meta_value() { 41 $t = $this->factory->term->create( array( 'taxonomy' => 'wptests_tax' ) ); 42 add_term_meta( $t, 'foo', 'bar' ); 43 add_term_meta( $t, 'foo', 'baz' ); 44 45 $this->assertTrue( delete_term_meta( $t, 'foo', 'bar' ) ); 46 47 $metas = get_term_meta( $t, 'foo', false ); 48 $this->assertSame( array( 'baz' ), $metas ); 49 } 50 51 public function test_get_with_no_key_should_fetch_all_keys() { 52 $t = $this->factory->term->create( array( 'taxonomy' => 'wptests_tax' ) ); 53 add_term_meta( $t, 'foo', 'bar' ); 54 add_term_meta( $t, 'foo1', 'baz' ); 55 56 $found = get_term_meta( $t ); 57 $expected = array( 58 'foo' => array( 'bar' ), 59 'foo1' => array( 'baz' ), 60 ); 61 62 $this->assertEqualSets( $expected, $found ); 63 } 64 65 public function test_get_with_key_should_fetch_all_for_key() { 66 $t = $this->factory->term->create( array( 'taxonomy' => 'wptests_tax' ) ); 67 add_term_meta( $t, 'foo', 'bar' ); 68 add_term_meta( $t, 'foo', 'baz' ); 69 add_term_meta( $t, 'foo1', 'baz' ); 70 71 $found = get_term_meta( $t, 'foo' ); 72 $expected = array( 'bar', 'baz' ); 73 74 $this->assertEqualSets( $expected, $found ); 75 } 76 77 public function test_get_should_respect_single_true() { 78 $t = $this->factory->term->create( array( 'taxonomy' => 'wptests_tax' ) ); 79 add_term_meta( $t, 'foo', 'bar' ); 80 add_term_meta( $t, 'foo', 'baz' ); 81 82 $found = get_term_meta( $t, 'foo', true ); 83 $this->assertEquals( 'bar', $found ); 84 } 85 86 public function test_update_should_pass_to_add_when_no_value_exists_for_key() { 87 $t = $this->factory->term->create( array( 'taxonomy' => 'wptests_tax' ) ); 88 89 $actual = update_term_meta( $t, 'foo', 'bar' ); 90 $this->assertInternalType( 'int', $actual ); 91 $this->assertNotEmpty( $actual ); 92 93 $meta = get_term_meta( $t, 'foo', true ); 94 $this->assertSame( 'bar', $meta ); 95 } 96 97 public function test_update_should_return_true_when_updating_existing_value_for_key() { 98 $t = $this->factory->term->create( array( 'taxonomy' => 'wptests_tax' ) ); 99 100 add_term_meta( $t, 'foo', 'bar' ); 101 102 $actual = update_term_meta( $t, 'foo', 'baz' ); 103 $this->assertTrue( $actual ); 104 105 $meta = get_term_meta( $t, 'foo', true ); 106 $this->assertSame( 'baz', $meta ); 107 } 108 109 public function test_term_meta_should_be_lazy_loaded_for_all_terms_in_wp_query_loop() { 110 global $wpdb; 111 112 $p = $this->factory->post->create( array( 'post_status' => 'publish' ) ); 113 114 register_taxonomy( 'wptests_tax', 'post' ); 115 $terms = $this->factory->term->create_many( 3, array( 'taxonomy' => 'wptests_tax' ) ); 116 wp_set_object_terms( $p, $terms, 'wptests_tax' ); 117 foreach ( $terms as $t ) { 118 add_term_meta( $t, 'foo', 'bar' ); 119 } 120 121 // Create another term, which should *not* be lazy loaded because it's unattached. 122 $orphan_term = $this->factory->term->create( array( 'taxonomy' => 'wptests_tax' ) ); 123 add_term_meta( $orphan_term, 'foo', 'bar' ); 124 125 $this->go_to( get_permalink( $p ) ); 126 127 if ( have_posts() ) { 128 while ( have_posts() ) { 129 the_post(); 130 131 // First request will hit the database. 132 $num_queries = $wpdb->num_queries; 133 $this->assertSame( 'bar', get_term_meta( $terms[0], 'foo', true ) ); 134 $this->assertSame( $num_queries + 1, $wpdb->num_queries ); 135 136 // Second and third requests should be in cache. 137 $this->assertSame( 'bar', get_term_meta( $terms[1], 'foo', true ) ); 138 $this->assertSame( 'bar', get_term_meta( $terms[2], 'foo', true ) ); 139 $this->assertSame( $num_queries + 1, $wpdb->num_queries ); 140 141 // Querying a term not primed should result in a hit. 142 $this->assertSame( 'bar', get_term_meta( $orphan_term, 'foo', true ) ); 143 $this->assertSame( $num_queries + 2, $wpdb->num_queries ); 144 } 145 } 146 } 147 } -
tests/phpunit/tests/term/wpGetObjectTerms.php
diff --git tests/phpunit/tests/term/wpGetObjectTerms.php tests/phpunit/tests/term/wpGetObjectTerms.php index 75d40bb..5843fc2 100644
class Tests_Term_WpGetObjectTerms extends WP_UnitTestCase { 418 418 $this->assertEqualSets( array( $t1, $t2 ), $found ); 419 419 } 420 420 421 /** 422 * @ticket 10142 423 */ 424 public function test_termmeta_cache_should_be_primed_by_default() { 425 global $wpdb; 426 427 register_taxonomy( 'wptests_tax', 'post' ); 428 $terms = $this->factory->term->create_many( 3, array( 'taxonomy' => 'wptests_tax' ) ); 429 add_term_meta( $terms[0], 'foo', 'bar' ); 430 add_term_meta( $terms[1], 'foo', 'bar' ); 431 add_term_meta( $terms[2], 'foo', 'bar' ); 432 433 $p = $this->factory->post->create(); 434 wp_set_object_terms( $p, $terms, 'wptests_tax' ); 435 436 $found = wp_get_object_terms( $p, 'wptests_tax' ); 437 438 $num_queries = $wpdb->num_queries; 439 440 foreach ( $terms as $t ) { 441 $this->assertSame( 'bar', get_term_meta( $t, 'foo', true ) ); 442 } 443 444 $this->assertSame( $num_queries, $wpdb->num_queries ); 445 } 446 447 /** 448 * @ticket 10142 449 */ 450 public function test_termmeta_cache_should_not_be_primed_when_update_term_meta_cache_is_false() { 451 global $wpdb; 452 453 register_taxonomy( 'wptests_tax', 'post' ); 454 $terms = $this->factory->term->create_many( 3, array( 'taxonomy' => 'wptests_tax' ) ); 455 add_term_meta( $terms[0], 'foo', 'bar' ); 456 add_term_meta( $terms[1], 'foo', 'bar' ); 457 add_term_meta( $terms[2], 'foo', 'bar' ); 458 459 $p = $this->factory->post->create(); 460 wp_set_object_terms( $p, $terms, 'wptests_tax' ); 461 462 $found = wp_get_object_terms( $p, 'wptests_tax', array( 463 'update_term_meta_cache' => false, 464 ) ); 465 466 $num_queries = $wpdb->num_queries; 467 468 foreach ( $terms as $t ) { 469 $this->assertSame( 'bar', get_term_meta( $t, 'foo', true ) ); 470 } 471 472 $this->assertSame( $num_queries + 3, $wpdb->num_queries ); 473 } 474 421 475 public function filter_get_object_terms( $terms ) { 422 476 $term_ids = wp_list_pluck( $terms, 'term_id' ); 423 477 // all terms should still be objects