diff --git a/src/wp-includes/taxonomy.php b/src/wp-includes/taxonomy.php index 9d82fba3d9d46..2e3ce8db04b88 100644 --- a/src/wp-includes/taxonomy.php +++ b/src/wp-includes/taxonomy.php @@ -2401,6 +2401,41 @@ function wp_get_object_terms( $object_ids, $taxonomies, $args = array() ) { return apply_filters( 'wp_get_object_terms', $terms, $object_ids, $taxonomies, $args ); } +/** + * Checks a term name against the maximum length allowed by the `wp_terms` table's `name` column. + * + * @since 7.2.0 + * @access private + * + * @global wpdb $wpdb WordPress database abstraction object. + * + * @param string $name Term name to check. + * @return true|WP_Error True if the name fits the column, WP_Error otherwise. + */ +function _wp_check_term_name_length( $name ) { + global $wpdb; + + $max_length = 200; + $col_length = $wpdb->get_col_length( $wpdb->terms, 'name' ); + + if ( is_array( $col_length ) && ! empty( $col_length['length'] ) ) { + $max_length = (int) $col_length['length']; + } + + if ( mb_strlen( $name ) > $max_length ) { + return new WP_Error( + 'term_name_too_long', + sprintf( + /* translators: %d: Maximum number of characters. */ + __( 'Term name may not be longer than %d characters.' ), + $max_length + ) + ); + } + + return true; +} + /** * Adds a new term to the database. * @@ -2520,6 +2555,11 @@ function wp_insert_term( $term, $taxonomy, $args = array() ) { return new WP_Error( 'invalid_term_name', __( 'Invalid term name.' ) ); } + $name_length_error = _wp_check_term_name_length( $name ); + if ( is_wp_error( $name_length_error ) ) { + return $name_length_error; + } + $slug_provided = ! empty( $args['slug'] ); if ( ! $slug_provided ) { $slug = sanitize_title( $name ); @@ -3312,6 +3352,11 @@ function wp_update_term( $term_id, $taxonomy, $args = array() ) { return new WP_Error( 'empty_term_name', __( 'A name is required for this term.' ) ); } + $name_length_error = _wp_check_term_name_length( $name ); + if ( is_wp_error( $name_length_error ) ) { + return $name_length_error; + } + if ( (int) $parsed_args['parent'] > 0 && ! term_exists( (int) $parsed_args['parent'] ) ) { return new WP_Error( 'missing_parent', __( 'Parent term does not exist.' ) ); } diff --git a/tests/phpunit/tests/term/wpInsertTerm.php b/tests/phpunit/tests/term/wpInsertTerm.php index 0bf95b9a10279..c4c620e25638c 100644 --- a/tests/phpunit/tests/term/wpInsertTerm.php +++ b/tests/phpunit/tests/term/wpInsertTerm.php @@ -907,6 +907,39 @@ public function test_wp_insert_term_with_empty_name_after_db_sanitization() { $this->assertSame( 'invalid_term_name', $term->get_error_code() ); } + /** + * @ticket 36610 + */ + public function test_wp_insert_term_name_too_long_should_return_wp_error() { + register_taxonomy( 'wptests_tax', 'post' ); + $found = wp_insert_term( str_repeat( 'a', 201 ), 'wptests_tax' ); + + $this->assertWPError( $found ); + $this->assertSame( 'term_name_too_long', $found->get_error_code() ); + } + + /** + * @ticket 36610 + */ + public function test_wp_insert_term_name_at_max_length_should_succeed() { + register_taxonomy( 'wptests_tax', 'post' ); + $found = wp_insert_term( str_repeat( 'a', 200 ), 'wptests_tax' ); + + $this->assertNotWPError( $found ); + } + + /** + * @ticket 36610 + */ + public function test_wp_insert_term_name_length_should_be_counted_in_characters_not_bytes() { + register_taxonomy( 'wptests_tax', 'post' ); + + // 200 multibyte characters fit the column, even though their byte length is far greater than 200. + $found = wp_insert_term( str_repeat( 'é', 200 ), 'wptests_tax' ); + + $this->assertNotWPError( $found ); + } + /** Helpers */ public function deleted_term_cb( $term, $tt_id, $taxonomy, $deleted_term, $object_ids ) { diff --git a/tests/phpunit/tests/term/wpUpdateTerm.php b/tests/phpunit/tests/term/wpUpdateTerm.php index cbbd5780d6d1c..b44bdae325245 100644 --- a/tests/phpunit/tests/term/wpUpdateTerm.php +++ b/tests/phpunit/tests/term/wpUpdateTerm.php @@ -802,4 +802,45 @@ public function test_wp_update_term_with_null_get_term() { $this->assertWPError( $found ); $this->assertSame( 'invalid_term', $found->get_error_code() ); } + + /** + * @ticket 36610 + */ + public function test_wp_update_term_name_too_long_should_return_wp_error() { + register_taxonomy( 'wptests_tax', 'post' ); + $t = self::factory()->term->create( array( 'taxonomy' => 'wptests_tax' ) ); + + $found = wp_update_term( + $t, + 'wptests_tax', + array( + 'name' => str_repeat( 'a', 201 ), + ) + ); + + _unregister_taxonomy( 'wptests_tax' ); + + $this->assertWPError( $found ); + $this->assertSame( 'term_name_too_long', $found->get_error_code() ); + } + + /** + * @ticket 36610 + */ + public function test_wp_update_term_name_at_max_length_should_succeed() { + register_taxonomy( 'wptests_tax', 'post' ); + $t = self::factory()->term->create( array( 'taxonomy' => 'wptests_tax' ) ); + + $found = wp_update_term( + $t, + 'wptests_tax', + array( + 'name' => str_repeat( 'a', 200 ), + ) + ); + + _unregister_taxonomy( 'wptests_tax' ); + + $this->assertNotWPError( $found ); + } }