How to avoid manage posts screen duplicates when two plugins use the same library

In your code, you add the filter like this:

add_action( 'restrict_manage_posts', array( $this, 'restrict_log_posts' ) );

Which adds a function that starts like this:

public function restrict_log_posts() {
    $type="wp_log";
    $taxonomy = 'wp_log_type';
    // only add filter to post type you want
    if ( isset( $_GET['post_type'] ) && esc_attr( $_GET['post_type'] ) === $type ) {
        // get wp_log_type
        $terms = get_terms(

First, this action passes the post type so it can be simplified by adjusting the add_action and using the first parameter it passes:

public function restrict_log_posts( $post_type ) {
    $type="wp_log";
    $taxonomy = 'wp_log_type';
    // only add filter to post type you want
    if ( 'wp_log' === $post_type ) {
        // get wp_log_type
        $terms = get_terms(

Then we could use did_action to only run it the first time:

public function restrict_log_posts( $post_type ) {
    if ( did_action( 'restrict_manage_posts' ) ) {
        return;
    }
....

But this might not have the desired effect, and it’s crude. Instead, this action doesn’t belong in this class at all, and should be a standalone action. Then we can define the function only if it isn’t already defined:


if ( !function_exists('restrict_logs_by_type') ) {
    add_action( 'restrict_manage_posts', 'restrict_logs_by_type', 10, 1 );
    /**
     * Add a filter form for the log admin so we can filter by wp_log_type taxonomy values
     *
     */
    function restrict_logs_by_type( $post_type ) {
        $type="wp_log";
        $taxonomy = 'wp_log_type';
        // only add filter to post type you want
        if ( $type !== $post_type ) {
            return;
        }
        // get wp_log_type
        $terms = get_terms([
                'taxonomy'   => $taxonomy,
                'hide_empty' => true,
        ]);
        if ( is_wp_error( $terms ) || empty( $terms ) ) {
            // no terms, or the taxonomy doesn't exist, skip
            return;
        }

        ?>
        <select name="wp_log_type">
            <option value=""><?php esc_html_e( 'All log types ', 'form-processor-mailchimp' ); ?></option>
            <?php
            $current_log_type = isset( $_GET[ $taxonomy ] ) ? esc_attr( $_GET[ $taxonomy ] ) : '';
            foreach ( $terms as $key => $term ) {
                printf(
                    '<option value="%s"%s>%s</option>',
                    esc_attr( $term->slug ),
                    selected( $term->slug, $current_log_type, false ),
                    esc_html( $term->name )
                );
            }
            ?>
        </select>
        <?php
    }
}

Note that I made a number of significant improvements:

  • I fixed a major security hole by adding escaping to your option tags
  • I replaced the ugly ternary operator with the selected function provided by WP
  • I replaced the if check with a !==, making it a guard. Now the entire function can be unindented by 1, and is more readable. Additionally both the Cyclomatic and NPath complexity have been reduced
  • The function is now standalone, there was never any need for it to be part of an object or class
  • I renamed the function to describe what it does
  • If no terms are found, or the taxonomy doesn’t exist, this version skips displaying the dropdown entirely. No sense having a dropdown if the only item is “All”. Without this, the log screen would crash if there were no log types or entries
  • The entire snippet is wrapped in a function_exists call, if the function is loaded by the first plugin, the second plugin won’t bother defining and adding it