Skip to main content
added 4610 characters in body
Source Link
emersonthis
  • 1.4k
  • 8
  • 20
  • 33

Full code from my complete plugin:

Paste this into a file in /wp-content/mu-plugins.php and then add/edit a post. You should see two new custom meta boxes "Foo" and "Bar" but you will only see "Bar". Comment in/out the final lines to test with different combinations of instances of the class.

<?php
/*
Plugin Name: SDP Custom Meta Boxes
Description: Defines a custom meta box framework class.
*/

class SDP_Custom_Field {

  public $name = NULL;
  public $slug = NULL;
  public $prefix = NULL;
  public $post_type = 'post'; //The type of Write screen on which to show the meta: 'post', 'page', 'dashboard', 'link', 'attachment' or 'custom_post_type'
  public $meta_box_location = 'normal'; //'normal', 'advanced', or 'side'
  public $meta_box_priority = 'default'; //'high', 'core', 'default' or 'low'
  public $meta_box_field_type = NULL;

  public function __construct( $custom_field_name, $args=array() ) {
    #set the name
    $this->name = $custom_field_name;

    #set the slug
    if (isset($args['slug'])) {
        $this->slug = $args['slug'];
    } else {
        $slug = strtolower($custom_field_name);
        $slug = str_replace(' ', '_', $slug);
        $this->slug = $slug;      
    }

    #set the meta_box_field_type
    if (isset($args['field_type'])) {
        switch ($args['field_type']) {
            case 'text':
                $this->meta_box_field_type = 'text';
                break;
            case 'textarea':
                $this->meta_box_field_type = 'textarea';
                break;            
            default:
                $this->meta_box_field_type = 'text';
                break;
        }

    } else {
        $this->meta_box_field_type = 'text';
    }

    // add_action( 'add_meta_boxes', array($this, '_add_custom_metaboxes'));
    // add_action( 'save_post', array($this, '_save_postdata'));

    add_action( 'add_meta_boxes', array(&$this, '_add_custom_metaboxes'));
    add_action( 'save_post', array(&$this, '_save_postdata'));

    //var_dump( is_object( array($this, '_add_custom_metaboxes') ));

  }

  // public function go() {
  //   add_action( 'add_meta_boxes', array($this, '_add_custom_metaboxes'));
  //   add_action( 'save_post', array($this, '_save_postdata'));
  // }

  #Echo the HTML for this meta box... 
  public function _print_field_HTML( $post ) {

      $value = get_post_meta( $post->ID, $this->slug, true );

      // Add an nonce field so we can check for it later.
      wp_nonce_field( $this->slug.'_custom_box', $this->slug.'_custom_box_nonce' );


      echo '<label for="'.$this->slug.'">'.$this->name.'</label> ';
      echo '<input type="text" id="'.$this->slug.'" name="'.$this->slug.'" value="' . esc_attr( $value ) . '" size="25" />';
  
      //TODO: Add update button
      //TODO: Add delet button
  }

  public function _add_custom_metaboxes() {
      #http://codex.wordpress.org/Function_Reference/add_meta_box
      add_meta_box( NULL, $this->name, array(&$this, '_print_field_HTML'), $this->post_type, $this->meta_box_location, $this->meta_box_priority, NULL );
  }
  



  /**
   * When the post is saved, saves our custom data.
   * @param int $post_id The ID of the post being saved.
   */
  public function _save_postdata( $post_id ) {

    /*
     * We need to verify this came from the our screen and with proper authorization,
     * because save_post can be triggered at other times.
     */

    // Check if our nonce is set.
    if ( ! isset( $_POST[$this->slug.'_custom_box_nonce'] ) )
      return $post_id;

    #http://codex.wordpress.org/Function_Reference/wp_nonce_field
    $nonce = $_POST[$this->slug.'_custom_box_nonce'];

    // Verify that the nonce is valid.
    if ( ! wp_verify_nonce( $nonce, $this->slug.'_custom_box' ) )
        return $post_id;

    // If this is an autosave, our form has not been submitted, so we don't want to do anything.
    if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) 
        return $post_id;


      if ( ! current_user_can( 'edit_post', $post_id ) )
          return $post_id;

    /* OK, its safe for us to save the data now. */

    // Sanitize user input.
    $mydata = sanitize_text_field( $_POST[$this->slug] );

    // Update the meta field in the database.
    update_post_meta( $post_id, $this->slug, $mydata );
  }

}

new SDP_Custom_Field( 'Foo' );
new SDP_Custom_Field( 'Bar' );

Full code from my complete plugin:

Paste this into a file in /wp-content/mu-plugins.php and then add/edit a post. You should see two new custom meta boxes "Foo" and "Bar" but you will only see "Bar". Comment in/out the final lines to test with different combinations of instances of the class.

<?php
/*
Plugin Name: SDP Custom Meta Boxes
Description: Defines a custom meta box framework class.
*/

class SDP_Custom_Field {

  public $name = NULL;
  public $slug = NULL;
  public $prefix = NULL;
  public $post_type = 'post'; //The type of Write screen on which to show the meta: 'post', 'page', 'dashboard', 'link', 'attachment' or 'custom_post_type'
  public $meta_box_location = 'normal'; //'normal', 'advanced', or 'side'
  public $meta_box_priority = 'default'; //'high', 'core', 'default' or 'low'
  public $meta_box_field_type = NULL;

  public function __construct( $custom_field_name, $args=array() ) {
    #set the name
    $this->name = $custom_field_name;

    #set the slug
    if (isset($args['slug'])) {
        $this->slug = $args['slug'];
    } else {
        $slug = strtolower($custom_field_name);
        $slug = str_replace(' ', '_', $slug);
        $this->slug = $slug;      
    }

    #set the meta_box_field_type
    if (isset($args['field_type'])) {
        switch ($args['field_type']) {
            case 'text':
                $this->meta_box_field_type = 'text';
                break;
            case 'textarea':
                $this->meta_box_field_type = 'textarea';
                break;            
            default:
                $this->meta_box_field_type = 'text';
                break;
        }

    } else {
        $this->meta_box_field_type = 'text';
    }

    // add_action( 'add_meta_boxes', array($this, '_add_custom_metaboxes'));
    // add_action( 'save_post', array($this, '_save_postdata'));

    add_action( 'add_meta_boxes', array(&$this, '_add_custom_metaboxes'));
    add_action( 'save_post', array(&$this, '_save_postdata'));

    //var_dump( is_object( array($this, '_add_custom_metaboxes') ));

  }

  // public function go() {
  //   add_action( 'add_meta_boxes', array($this, '_add_custom_metaboxes'));
  //   add_action( 'save_post', array($this, '_save_postdata'));
  // }

  #Echo the HTML for this meta box... 
  public function _print_field_HTML( $post ) {

      $value = get_post_meta( $post->ID, $this->slug, true );

      // Add an nonce field so we can check for it later.
      wp_nonce_field( $this->slug.'_custom_box', $this->slug.'_custom_box_nonce' );


      echo '<label for="'.$this->slug.'">'.$this->name.'</label> ';
      echo '<input type="text" id="'.$this->slug.'" name="'.$this->slug.'" value="' . esc_attr( $value ) . '" size="25" />';
  
      //TODO: Add update button
      //TODO: Add delet button
  }

  public function _add_custom_metaboxes() {
      #http://codex.wordpress.org/Function_Reference/add_meta_box
      add_meta_box( NULL, $this->name, array(&$this, '_print_field_HTML'), $this->post_type, $this->meta_box_location, $this->meta_box_priority, NULL );
  }
  



  /**
   * When the post is saved, saves our custom data.
   * @param int $post_id The ID of the post being saved.
   */
  public function _save_postdata( $post_id ) {

    /*
     * We need to verify this came from the our screen and with proper authorization,
     * because save_post can be triggered at other times.
     */

    // Check if our nonce is set.
    if ( ! isset( $_POST[$this->slug.'_custom_box_nonce'] ) )
      return $post_id;

    #http://codex.wordpress.org/Function_Reference/wp_nonce_field
    $nonce = $_POST[$this->slug.'_custom_box_nonce'];

    // Verify that the nonce is valid.
    if ( ! wp_verify_nonce( $nonce, $this->slug.'_custom_box' ) )
        return $post_id;

    // If this is an autosave, our form has not been submitted, so we don't want to do anything.
    if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) 
        return $post_id;


      if ( ! current_user_can( 'edit_post', $post_id ) )
          return $post_id;

    /* OK, its safe for us to save the data now. */

    // Sanitize user input.
    $mydata = sanitize_text_field( $_POST[$this->slug] );

    // Update the meta field in the database.
    update_post_meta( $post_id, $this->slug, $mydata );
  }

}

new SDP_Custom_Field( 'Foo' );
new SDP_Custom_Field( 'Bar' );
added 170 characters in body
Source Link
emersonthis
  • 1.4k
  • 8
  • 20
  • 33

I'm writing a plugin which defines a class that is meant to be called multiple times. The class uses add_action to register it's own methods as callbacks.

class MyClass {

    __construct($foo) {

        add_action('hook1', array($this, 'method1') );
        add_action('hook2', array($this, 'method2') );

    }

    public function method1() {...}

    public function method2() {...}

}

This works just fine. But when I do it twice, only the second works.

new MyClass('foo');
new MyClass('bar');

In other words, the results of the code above are exactly what I'd expect from the following:

// new MyClass('foo');
new MyClass('bar');

It seems like the second instance overrides the first. What am I doing wrong?

I've checked to make sure that spl_object_hash() is different for both instances and it is.

I added var_dump($wp_filter); after my code and I see BOTH instances listed under the relevant action hook key. So it looks like WP knows they are both there...

I'm writing a plugin which defines a class that is meant to be called multiple times. The class uses add_action to register it's own methods as callbacks.

class MyClass {

    __construct($foo) {

        add_action('hook1', array($this, 'method1') );
        add_action('hook2', array($this, 'method2') );

    }

    public function method1() {...}

    public function method2() {...}

}

This works just fine. But when I do it twice, only the second works.

new MyClass('foo');
new MyClass('bar');

In other words, the results of the code above are exactly what I'd expect from the following:

// new MyClass('foo');
new MyClass('bar');

It seems like the second instance overrides the first. What am I doing wrong?

I've checked to make sure that spl_object_hash() is different for both instances and it is.

I'm writing a plugin which defines a class that is meant to be called multiple times. The class uses add_action to register it's own methods as callbacks.

class MyClass {

    __construct($foo) {

        add_action('hook1', array($this, 'method1') );
        add_action('hook2', array($this, 'method2') );

    }

    public function method1() {...}

    public function method2() {...}

}

This works just fine. But when I do it twice, only the second works.

new MyClass('foo');
new MyClass('bar');

In other words, the results of the code above are exactly what I'd expect from the following:

// new MyClass('foo');
new MyClass('bar');

It seems like the second instance overrides the first. What am I doing wrong?

I've checked to make sure that spl_object_hash() is different for both instances and it is.

I added var_dump($wp_filter); after my code and I see BOTH instances listed under the relevant action hook key. So it looks like WP knows they are both there...

deleted 595 characters in body
Source Link
emersonthis
  • 1.4k
  • 8
  • 20
  • 33

I'm writing a plugin which defines a class that is meant to be called multiple times. The class uses add_action to register it's own methods as callbacks.

class MyClass {

    __construct($foo) {

        add_action('hook1', array($this, 'method1') );
        add_action('hook2', array($this, 'method2') );

    }

    public function method1() {...}

    public function method2() {...}

}

This works just fine. But when I do it twice, only the second works.

new MyClass('foo');
new MyClass('bar');

In other words, the results of the code above are exactly what I'd expect from the following:

// new MyClass('foo');
new MyClass('bar');

It seems like the second instance overrides the first. What am I doing wrong?

More information

This issue does appear to be linked I've checked to the way Wordpress registers actions. I'm currently on Wordpress v3.8.1 and I'm looking in /wp-includes/plugin.php. I seemake sure that when I register an action with add_action() (line 360) it is basically just a wrapper for add_filter() (line 82) which creates a unique $idx by calling _wp_filter_build_unique_idspl_object_hash() (line 774). When I add debug code to print out the $idx, I see that it is in fact the samedifferent for both instances of my class. Soand it is confirmed that the subsequent instances of my class ARE overriding the previous(s). The remaining question is how to write my class so that this problem doesn't happen.

I'm writing a plugin which defines a class that is meant to be called multiple times. The class uses add_action to register it's own methods as callbacks.

class MyClass {

    __construct($foo) {

        add_action('hook1', array($this, 'method1') );
        add_action('hook2', array($this, 'method2') );

    }

    public function method1() {...}

    public function method2() {...}

}

This works just fine. But when I do it twice, only the second works.

new MyClass('foo');
new MyClass('bar');

In other words, the results of the code above are exactly what I'd expect from the following:

// new MyClass('foo');
new MyClass('bar');

It seems like the second instance overrides the first. What am I doing wrong?

More information

This issue does appear to be linked to the way Wordpress registers actions. I'm currently on Wordpress v3.8.1 and I'm looking in /wp-includes/plugin.php. I see that when I register an action with add_action() (line 360) it is basically just a wrapper for add_filter() (line 82) which creates a unique $idx by calling _wp_filter_build_unique_id() (line 774). When I add debug code to print out the $idx, I see that it is in fact the same for both instances of my class. So it is confirmed that the subsequent instances of my class ARE overriding the previous(s). The remaining question is how to write my class so that this problem doesn't happen.

I'm writing a plugin which defines a class that is meant to be called multiple times. The class uses add_action to register it's own methods as callbacks.

class MyClass {

    __construct($foo) {

        add_action('hook1', array($this, 'method1') );
        add_action('hook2', array($this, 'method2') );

    }

    public function method1() {...}

    public function method2() {...}

}

This works just fine. But when I do it twice, only the second works.

new MyClass('foo');
new MyClass('bar');

In other words, the results of the code above are exactly what I'd expect from the following:

// new MyClass('foo');
new MyClass('bar');

It seems like the second instance overrides the first. What am I doing wrong?

I've checked to make sure that spl_object_hash() is different for both instances and it is.

deleted 159 characters in body
Source Link
emersonthis
  • 1.4k
  • 8
  • 20
  • 33
Loading
added 851 characters in body
Source Link
emersonthis
  • 1.4k
  • 8
  • 20
  • 33
Loading
Source Link
emersonthis
  • 1.4k
  • 8
  • 20
  • 33
Loading