Script to duplicate categories as tags

As I said, we would rather use native functions here, which is safer and already does all the hard work for you. We need to be very careful here as this is a very expensive operation to run. Not doing this correctly can crash your site due to timing out

Your worksflow is also wrong here. I would first create all the tags, and after that, insert the tags into the posts. You are trying to do all at once.

Lets us first grab all the categories and use that info to create tags. Just note:

  • post_tag being a non hierarchical taxonomy cannot have any duplicate names, hierarchical taxonomies like category can have duplicate term names within different hierarchies. If you have categories with same names, you will get a WP_Error object back if you are going to try to insert tags with duplicate names. To avoid that, we will simply check if a tag exists before we try to insert it

  • Tags cannot have parent/child relationships, categories can. So when we duplicate a category to be a term, we should set all parents explicitly to 0

Here is the code: (NOTE: We would require PHP 5.4+)

// Lets create our tags. We will use wp_insert_term hooked to init
add_action( 'init', function ()
{
    // Get all categories
    $categories = get_categories( ['hide_empty' => 0] );

    // Probably totally unnecessary, but check for empty $categories
    if ( !$categories )
        return;

    // Loop through the categories and create our tags
    foreach ( $categories as $category ) {
        // First make sure that there is no tag that exist with the same name
        if ( term_exists( $category->name, 'post_tag' ) )
            continue;

        // Set our arguments for our terms from $category
        $args = [
            'description' => $category->description,
            'parent'      => 0, // We can drop this, default is 0
            'slug'        => $category->slug,
        ];
        wp_insert_term( $category->name, 'post_tag', $args );
    } //endforeach
}, PHP_INT_MAX );

You should now see these tags in your Tag admin page. You can also remove the code as it is no more necessary.

Now that we have our tags created, we need to add them to our posts. Here we would need to be careful as you are talking about 900+ posts, so we do not want to break the bank.

What we will do is, we will query all of those posts, but only the post ID’s which saves a lot on resources. Also, we will use get_the_category which are cached, so it does not require extra db calls, wp_get_post_categories are not cached, so it is really expensive

Due to wp_set_post_terms being expensive, you would maybe need to split the following into a couple of queries, but I believe one query should be enough

Lets attach the tags to our posts

add_action( 'template_redirect', function ()
{
    // Get all our posts
    $args = [
        'posts_per_page' => -1,
        'fields'         => 'ids', // Make the query lean
        // Add any additional query args here
    ];
    $q = new WP_Query( $args );

    if ( !$q->have_posts() )
        return;

    while ( $q->have_posts() ) {
    $q->the_post();

        // Get all the categories attached to the post
        $categories = get_the_category();
        if ( !$categories )
            continue;

        // Get the names from the categories into an array
        $names = wp_list_pluck( $categories, 'name' );
        // Loop through the names and make sure that the post does not already has the tag attached
        foreach ( $names as $key=>$name ) {
            if ( has_tag( $name ) )
                unset ( $names[$key] );
        }
        // Make sure we still have a valid $names array
        if ( !$names )
            continue; 

        // Finally, attach our tags to the posts
        wp_set_post_terms( 
            get_the_ID(), // Post ID 
            $names, // Array of tag names to attach
            'post_tag',
            true // Only add the tags, do not override
        );
    }
    wp_reset_postdata();
});

Your posts should now have tags attached to them matching the categories. You can also now remove the function as we are done