+ $args['object_type'] = array_unique( (array) $object_type );
+
+ $args['labels'] = get_taxonomy_labels( (object) $args );
+ $args['label'] = $args['labels']->name;
+
+ // If not set, use the default meta box
+ if ( null === $args['meta_box_cb'] ) {
+ if ( $args['hierarchical'] )
+ $args['meta_box_cb'] = 'post_categories_meta_box';
+ else
+ $args['meta_box_cb'] = 'post_tags_meta_box';
+ }
+
+ $wp_taxonomies[ $taxonomy ] = (object) $args;
+
+ // register callback handling for metabox
+ add_filter( 'wp_ajax_add-' . $taxonomy, '_wp_ajax_add_hierarchical_term' );
+
+ /**
+ * Fires after a taxonomy is registered.
+ *
+ * @since 3.3.0
+ *
+ * @param string $taxonomy Taxonomy slug.
+ * @param array|string $object_type Object type or array of object types.
+ * @param array $args Array of taxonomy registration arguments.
+ */
+ do_action( 'registered_taxonomy', $taxonomy, $object_type, $args );
+}
+
+/**
+ * Unregisters a taxonomy.
+ *
+ * Can not be used to unregister built-in taxonomies.
+ *
+ * @since 4.5.0
+ *
+ * @global WP $wp Current WordPress environment instance.
+ * @global array $wp_taxonomies List of taxonomies.
+ *
+ * @param string $taxonomy Taxonomy name.
+ * @return bool|WP_Error True on success, WP_Error on failure or if the taxonomy doesn't exist.
+ */
+function unregister_taxonomy( $taxonomy ) {
+ if ( ! taxonomy_exists( $taxonomy ) ) {
+ return new WP_Error( 'invalid_taxonomy', __( 'Invalid taxonomy' ) );
+ }
+
+ $taxonomy_args = get_taxonomy( $taxonomy );
+
+ // Do not allow unregistering internal taxonomies.
+ if ( $taxonomy_args->_builtin ) {
+ return new WP_Error( 'invalid_taxonomy', __( 'Unregistering a built-in taxonomy is not allowed' ) );
+ }
+
+ global $wp, $wp_taxonomies;
+
+ // Remove query var.
+ if ( false !== $taxonomy_args->query_var ) {
+ $wp->remove_query_var( $taxonomy_args->query_var );
+ }
+
+ // Remove rewrite tags and permastructs.
+ if ( false !== $taxonomy_args->rewrite ) {
+ remove_rewrite_tag( "%$taxonomy%" );
+ remove_permastruct( $taxonomy );
+ }
+
+ // Unregister callback handling for metabox.
+ remove_filter( 'wp_ajax_add-' . $taxonomy, '_wp_ajax_add_hierarchical_term' );
+
+ // Remove the taxonomy.
+ unset( $wp_taxonomies[ $taxonomy ] );
+
+ /**
+ * Fires after a taxonomy is unregistered.
+ *
+ * @since 4.5.0
+ *
+ * @param string $taxonomy Taxonomy name.
+ */
+ do_action( 'unregistered_taxonomy', $taxonomy );
+
+ return true;
+}
+
+/**
+ * Builds an object with all taxonomy labels out of a taxonomy object
+ *
+ * Accepted keys of the label array in the taxonomy object:
+ *
+ * - name - general name for the taxonomy, usually plural. The same as and overridden by $tax->label. Default is Tags/Categories
+ * - singular_name - name for one object of this taxonomy. Default is Tag/Category
+ * - search_items - Default is Search Tags/Search Categories
+ * - popular_items - This string isn't used on hierarchical taxonomies. Default is Popular Tags
+ * - all_items - Default is All Tags/All Categories
+ * - parent_item - This string isn't used on non-hierarchical taxonomies. In hierarchical ones the default is Parent Category
+ * - parent_item_colon - The same as `parent_item`, but with colon `:` in the end
+ * - edit_item - Default is Edit Tag/Edit Category
+ * - view_item - Default is View Tag/View Category
+ * - update_item - Default is Update Tag/Update Category
+ * - add_new_item - Default is Add New Tag/Add New Category
+ * - new_item_name - Default is New Tag Name/New Category Name
+ * - separate_items_with_commas - This string isn't used on hierarchical taxonomies. Default is "Separate tags with commas", used in the meta box.
+ * - add_or_remove_items - This string isn't used on hierarchical taxonomies. Default is "Add or remove tags", used in the meta box when JavaScript is disabled.
+ * - choose_from_most_used - This string isn't used on hierarchical taxonomies. Default is "Choose from the most used tags", used in the meta box.
+ * - not_found - Default is "No tags found"/"No categories found", used in the meta box and taxonomy list table.
+ * - no_terms - Default is "No tags"/"No categories", used in the posts and media list tables.
+ * - items_list_navigation - String for the table pagination hidden heading.
+ * - items_list - String for the table hidden heading.
+ *
+ * Above, the first default value is for non-hierarchical taxonomies (like tags) and the second one is for hierarchical taxonomies (like categories).
+ *
+ * @todo Better documentation for the labels array.
+ *
+ * @since 3.0.0
+ * @since 4.3.0 Added the `no_terms` label.
+ * @since 4.4.0 Added the `items_list_navigation` and `items_list` labels.
+ *
+ * @param object $tax Taxonomy object.
+ * @return object object with all the labels as member variables.
+ */
+function get_taxonomy_labels( $tax ) {
+ $tax->labels = (array) $tax->labels;
+
+ if ( isset( $tax->helps ) && empty( $tax->labels['separate_items_with_commas'] ) )
+ $tax->labels['separate_items_with_commas'] = $tax->helps;
+
+ if ( isset( $tax->no_tagcloud ) && empty( $tax->labels['not_found'] ) )
+ $tax->labels['not_found'] = $tax->no_tagcloud;
+
+ $nohier_vs_hier_defaults = array(
+ 'name' => array( _x( 'Tags', 'taxonomy general name' ), _x( 'Categories', 'taxonomy general name' ) ),
+ 'singular_name' => array( _x( 'Tag', 'taxonomy singular name' ), _x( 'Category', 'taxonomy singular name' ) ),
+ 'search_items' => array( __( 'Search Tags' ), __( 'Search Categories' ) ),
+ 'popular_items' => array( __( 'Popular Tags' ), null ),
+ 'all_items' => array( __( 'All Tags' ), __( 'All Categories' ) ),
+ 'parent_item' => array( null, __( 'Parent Category' ) ),
+ 'parent_item_colon' => array( null, __( 'Parent Category:' ) ),
+ 'edit_item' => array( __( 'Edit Tag' ), __( 'Edit Category' ) ),
+ 'view_item' => array( __( 'View Tag' ), __( 'View Category' ) ),
+ 'update_item' => array( __( 'Update Tag' ), __( 'Update Category' ) ),
+ 'add_new_item' => array( __( 'Add New Tag' ), __( 'Add New Category' ) ),
+ 'new_item_name' => array( __( 'New Tag Name' ), __( 'New Category Name' ) ),
+ 'separate_items_with_commas' => array( __( 'Separate tags with commas' ), null ),
+ 'add_or_remove_items' => array( __( 'Add or remove tags' ), null ),
+ 'choose_from_most_used' => array( __( 'Choose from the most used tags' ), null ),
+ 'not_found' => array( __( 'No tags found.' ), __( 'No categories found.' ) ),
+ 'no_terms' => array( __( 'No tags' ), __( 'No categories' ) ),
+ 'items_list_navigation' => array( __( 'Tags list navigation' ), __( 'Categories list navigation' ) ),
+ 'items_list' => array( __( 'Tags list' ), __( 'Categories list' ) ),
+ );
+ $nohier_vs_hier_defaults['menu_name'] = $nohier_vs_hier_defaults['name'];
+
+ $labels = _get_custom_object_labels( $tax, $nohier_vs_hier_defaults );
+
+ $taxonomy = $tax->name;
+
+ $default_labels = clone $labels;
+
+ /**
+ * Filter the labels of a specific taxonomy.
+ *
+ * The dynamic portion of the hook name, `$taxonomy`, refers to the taxonomy slug.
+ *
+ * @since 4.4.0
+ *
+ * @see get_taxonomy_labels() for the full list of taxonomy labels.
+ *
+ * @param object $labels Object with labels for the taxonomy as member variables.
+ */
+ $labels = apply_filters( "taxonomy_labels_{$taxonomy}", $labels );
+
+ // Ensure that the filtered labels contain all required default values.
+ $labels = (object) array_merge( (array) $default_labels, (array) $labels );
+
+ return $labels;
+}
+
+/**
+ * Add an already registered taxonomy to an object type.
+ *
+ * @since 3.0.0
+ *
+ * @global array $wp_taxonomies The registered taxonomies.
+ *
+ * @param string $taxonomy Name of taxonomy object.
+ * @param string $object_type Name of the object type.
+ * @return bool True if successful, false if not.
+ */
+function register_taxonomy_for_object_type( $taxonomy, $object_type) {
+ global $wp_taxonomies;
+
+ if ( !isset($wp_taxonomies[$taxonomy]) )
+ return false;
+
+ if ( ! get_post_type_object($object_type) )
+ return false;
+
+ if ( ! in_array( $object_type, $wp_taxonomies[$taxonomy]->object_type ) )
+ $wp_taxonomies[$taxonomy]->object_type[] = $object_type;
+
+ // Filter out empties.
+ $wp_taxonomies[ $taxonomy ]->object_type = array_filter( $wp_taxonomies[ $taxonomy ]->object_type );
+
+ return true;
+}
+
+/**
+ * Remove an already registered taxonomy from an object type.
+ *
+ * @since 3.7.0
+ *
+ * @global array $wp_taxonomies The registered taxonomies.
+ *
+ * @param string $taxonomy Name of taxonomy object.
+ * @param string $object_type Name of the object type.
+ * @return bool True if successful, false if not.
+ */
+function unregister_taxonomy_for_object_type( $taxonomy, $object_type ) {
+ global $wp_taxonomies;
+
+ if ( ! isset( $wp_taxonomies[ $taxonomy ] ) )
+ return false;
+
+ if ( ! get_post_type_object( $object_type ) )
+ return false;
+
+ $key = array_search( $object_type, $wp_taxonomies[ $taxonomy ]->object_type, true );
+ if ( false === $key )
+ return false;
+
+ unset( $wp_taxonomies[ $taxonomy ]->object_type[ $key ] );
+ return true;