I've started my first plugin and I need to store values returned from an API call to a database. But before I do that, I need to be able to make a database table. I have the following code in a file that is required of the main plugin file:

register_activation_hook(__FILE__, 'PLUGIN_activation');
function PLUGIN_activation() {
    //setup DB Tables
    global $wpdb;

    $table_name = $wpdb->prefix . "table_name";//insert actual tablename here

    $charset_collate = $wpdb->get_charset_collate();

    $sql = "CREATE TABLE $table_name (
      id mediumint(9) NOT NULL AUTO_INCREMENT,
      event_id text NOT NULL,
      event_date mediumint(8) DEFAULT '00000000' NOT NULL,
      event_unique mediumint(13) NOT NULL,
      name tinytext NOT NULL,
      PRIMARY KEY  (id)
    ) $charset_collate;";

    require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
    dbDelta( $sql );

I have reinstalled the plugin and activated it, but nothing happened.

For some reason, the table is not being created. What could be stopping this?

1 Answer 1


Note that the $file in register_activation_hook( $file, $callback ), should be equal to path to the main plugin file, but when you have it in a sub file as __FILE__ then it's not the same! That means your callback is never called.

I would also recommend prefixing the function's name to avoid possible name collision or use namespaces.

More in the Codex here.


It's informative to peek into the function's definition:

 * Set the activation hook for a plugin.
 * When a plugin is activated, the action 'activate_PLUGINNAME' hook is
 * called. In the name of this hook, PLUGINNAME is replaced with the name
 * of the plugin, including the optional subdirectory. For example, when the
 * plugin is located in wp-content/plugins/sampleplugin/sample.php, then
 * the name of this hook will become 'activate_sampleplugin/sample.php'.
 * When the plugin consists of only one file and is (as by default) located at
 * wp-content/plugins/sample.php the name of this hook will be
 * 'activate_sample.php'.
 * @since 2.0.0
 * @param string   $file     The filename of the plugin including the path.
 * @param callable $function The function hooked to the 'activate_PLUGIN' action.
function register_activation_hook($file, $function) {
    $file = plugin_basename($file);
    add_action('activate_' . $file, $function);

and this is the corresponding do_action call for the plugin acitvation in the activate_plugin() function:

 * Fires as a specific plugin is being activated.
 * This hook is the "activation" hook used internally by register_activation_hook().
 * The dynamic portion of the hook name, `$plugin`, refers to the plugin basename.
 * If a plugin is silently activated (such as during an update), this hook does not fire.
 * @since 2.0.0
 * @param bool $network_wide Whether to enable the plugin for all sites in the network
 *                           or just the current site. Multisite only. Default is false.
  do_action( "activate_{$plugin}", $network_wide );


Let's walk through this with a real plugin.

So take for example the WooCommerce plugin, that has the main plugin file:


where it defines:

register_activation_hook( __FILE__, array( 'WC_Install', 'install' ) );

where $file is __FILE__ or


Then the line:

$file = plugin_basename( $file );


$file = plugin_basename( '/full/path/to/wp-content/woocommerce/woocommerce.php' );

that gives:

$file = 'woocommerce/woocommerce.php';

Then the dynamic hook:

add_action('activate_' . $file, $function);


add_action('activate_woocommerce/woocommerce.php', $function);

So if WooCommerce would place the registration activation hook in a special file under e.g.


then this would be the value of __FILE__ and we would be registering the following action:

add_action('activate_woocommerce/include/activation.php', $function);

but there's no do_action() call for that one.

Instead they might store the main plugin file path, to use it elsewhere, like they already do in the main plugin file here:

$this->define( 'WC_PLUGIN_FILE', __FILE__ );

Hope it helps!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.