php - Using register_activation_hook in classes

I am trying to develop a plugin for basic SEO purposes, as a lot of people I know don't like using Yoast. I literal

I am trying to develop a plugin for basic SEO purposes, as a lot of people I know don't like using Yoast. I literally just started the plugin, and am building out the activation message displayed to the user when they activate the plugin. I am having trouble with a mix between OOP and built-in Wordpress functions, and not sure where I am going wrong.

The only way I can get this to work, is by creating a new instance of the SEO_Plugin_Activation class inside my main files constructor, and then calling the activatePlugin method in that class. I feel like this is unnecessary. I also think how I am executing my functions in the activation class file don't really make much sense either. But it's the only way I can get it to work right now.

I am not sure if what I am doing is because I am not grasping OOP techniques 100%, or if I am not utilizing Wordpress API correctly. I have included three code examples in the following order:

  1. Main plugin file
  2. Class file that handles my activation requirements
  3. What I really was hoping could be done.

seo.php (main plugin file)

<?php
/*
... generic plugin info
*/

require_once(dirname(__FILE__) . '/admin/class-plugin-activation.php');

class SEO {
  function __construct() {
    $activate = new SEO_Plugin_Activation();
    $activate->activatePlugin();
  }

}

new SEO();
?>

class-plugin-activation.php

<?php
class SEO_Plugin_Activation {

  function __construct() {
    register_activation_hook(__FILE__ . '../seo.php', array($this, 'activatePlugin'));
    add_action('admin_notices', array($this, 'showSitemapInfo'));
  }

  function activatePlugin() {
    set_transient('show_sitemap_info', true, 5);
  }

  function showSitemapInfo() {
    if(get_transient('show_sitemap_info')) {
      echo '<div class="updated notice is-dismissible">' .
              'Your sitemap files can be found at these following links: ' .
            '</div>';
      delete_transient('show_sitemap_info');
    }
  }

}
?>

seo.php(What I was hoping for)

<?php
/*
 ... blah blah blah
*/

require_once(dirname(__FILE__) . '/admin/class-plugin-activation.php');

class SEO {
  function __construct() {
    register_activation_hook(__FILE__, array($this, 'wp_install'));
  }

  function wp_install() {
    $activate = new SEO_Plugin_Activation();
    // Execute some method(s) here that would take care 
    // of all of my plugin activation bootstrapping
  }

}

new SEO();
?>

I tried doing it the way I outline in the third script, but am having no success. As of right now, the message does display properly, with no error messages.

I am trying to develop a plugin for basic SEO purposes, as a lot of people I know don't like using Yoast. I literally just started the plugin, and am building out the activation message displayed to the user when they activate the plugin. I am having trouble with a mix between OOP and built-in Wordpress functions, and not sure where I am going wrong.

The only way I can get this to work, is by creating a new instance of the SEO_Plugin_Activation class inside my main files constructor, and then calling the activatePlugin method in that class. I feel like this is unnecessary. I also think how I am executing my functions in the activation class file don't really make much sense either. But it's the only way I can get it to work right now.

I am not sure if what I am doing is because I am not grasping OOP techniques 100%, or if I am not utilizing Wordpress API correctly. I have included three code examples in the following order:

  1. Main plugin file
  2. Class file that handles my activation requirements
  3. What I really was hoping could be done.

seo.php (main plugin file)

<?php
/*
... generic plugin info
*/

require_once(dirname(__FILE__) . '/admin/class-plugin-activation.php');

class SEO {
  function __construct() {
    $activate = new SEO_Plugin_Activation();
    $activate->activatePlugin();
  }

}

new SEO();
?>

class-plugin-activation.php

<?php
class SEO_Plugin_Activation {

  function __construct() {
    register_activation_hook(__FILE__ . '../seo.php', array($this, 'activatePlugin'));
    add_action('admin_notices', array($this, 'showSitemapInfo'));
  }

  function activatePlugin() {
    set_transient('show_sitemap_info', true, 5);
  }

  function showSitemapInfo() {
    if(get_transient('show_sitemap_info')) {
      echo '<div class="updated notice is-dismissible">' .
              'Your sitemap files can be found at these following links: ' .
            '</div>';
      delete_transient('show_sitemap_info');
    }
  }

}
?>

seo.php(What I was hoping for)

<?php
/*
 ... blah blah blah
*/

require_once(dirname(__FILE__) . '/admin/class-plugin-activation.php');

class SEO {
  function __construct() {
    register_activation_hook(__FILE__, array($this, 'wp_install'));
  }

  function wp_install() {
    $activate = new SEO_Plugin_Activation();
    // Execute some method(s) here that would take care 
    // of all of my plugin activation bootstrapping
  }

}

new SEO();
?>

I tried doing it the way I outline in the third script, but am having no success. As of right now, the message does display properly, with no error messages.

Share Improve this question edited Aug 10, 2017 at 14:04 Tom J Nowell 61.2k7 gold badges79 silver badges150 bronze badges asked Aug 10, 2017 at 13:36 Dan ZuzevichDan Zuzevich 2031 gold badge2 silver badges6 bronze badges 4
  • Are you looking for reassurance or the correct way to do OO in WordPress? I'm afraid there is no canonical path to follow here, the only things I can share are generic OO things. E.g. don't create SEO_Plugin_Activation inside your SEO class, employ the dependency injection and pass it as an argument instead, and don't define and use a class in the same file, loading a file describing a class shouldn't also run it else it's impossible to write unit tests – Tom J Nowell Commented Aug 10, 2017 at 13:47
  • Thanks for the reply. I honestly don't care too much about sticking to the "100% Wordpress Way". Judging by your comment, it looks like I clearly lack some understanding of OOP principles. I was under the impression that I needed to create a new class inside of a class to call its methods. – Dan Zuzevich Commented Aug 10, 2017 at 13:50
  • You can do that, but it's not as flexible, e.g. if you want to test the SEO class, how would you replace the SEO_Plugin_Activation class with a mock object without modifying it? Anyway it seems this question is about a misunderstanding of how register_activation_hook works – Tom J Nowell Commented Aug 10, 2017 at 14:04
  • Got ya, thanks alot. I honestly don't think I will be performing tests on anything as that's a little over my head at the moment. – Dan Zuzevich Commented Aug 10, 2017 at 14:21
Add a comment  | 

4 Answers 4

Reset to default 14

Having reread your question, I think I see the issue, and it stems from a misunderstanding of how register_activation_hook works, combined with some confusion over how you're bootstrapping your code and what it means to bootstrap

Part 1: register_activation_hook

This function takes 2 parameters:

register_activation_hook( string $file, callable $function )

The first parameter, $file is the main plugin file, not the file that contains what you want to run. It's used the same way as plugins_url, so you need the value of __FILE__, specifically its value in the root plugin file with your plugin header.

The second parameter is a callable and works as you expect, but it's really just using add_action internally

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’.

Part 2: Bootstrapping and __FILE__

A fundamental problem here is that __FILE__ will have different values in different locations, and you need a specific value.

You also have a problem, that bootstrapping should assemble the object graph, but you don't do that. All your objects are created as soon as they're defined, or created inside eachother, making it difficult or impossible to pass values to them.

As an example, I could write a plugin like this:

plugin.php:

<?php
/**
 * Plugin Name: My Plugin
 * Version: 0.1
 */

// loading step
require_once( 'php/app.php' );

// bootstrapping step
$app = new App( __FILE__ );

// execution step
$app->run();

I defined all my classes in the php subfolder, and loaded them all in the same place. PHP now knows what my classes are, their names etc, but nothing has happened yet.

I then create all my objects, passing them what they need, in this case App needs the value of __FILE__ so I pass it along. Note that this creates the objects in memory, but doesn't do any work. The plugins application is ready to go, to pounce into action, but it's in the preparation phase.

The next step may be to pipe these objects into a set of unit tests, but now I'm going to run them. I've finished my bootstrapping process, and the application is ready to run, so I trigger the run method. I shouldn't need to pass anything to run as everything necessary has been passed to the constructors. During the run method, I add all my filters and hooks. It's during those hooks that all the other parts of the plugin run.

The important part though is that I have a defined structure, and that I pass in what's necessary during the construction/bootstrapping phase, with a defined life cycle

You can set a const who will conation the __FILE__ value and use in wherever you want in you classes.

Example:

main-plugin-file.php

<?php
/**
* Plugin Name: Plugin with Classes
*/

define( 'PLUGIN_WITH_CLASSES__FILE__', __FILE__ );

include( 'my-class.php' );

my-class.php

<?php
class myClass {
  function __construct() {
    // Run this on plugin activation
    register_activation_hook( PLUGIN_WITH_CLASSES__FILE__,  [ $this, 'create_plugin_database_table' ] );
  }

  function create_plugin_database_table(){
    //Create DB Table ...
  }
}

new myClass();

You really need to be registering your activation/deactivation/uninstall hooks outside of your plugin class as per kaiser's answer here, which provides a much better rundown on the subject than I could write.

That should cover you if you're looking to write this plugin as a learning exercise. If all you're looking for is an SEO plugin that works well and isn't plastered with tacky ads like Yoast, I can highly recommend The SEO Framework.

Tom McFarlin created a very extensive plugin boilerplate (now maintained by Devin Vinson), all written in OOP, that you can use to either create your new plugin, or just study the application flow to answer your question. I have used it for several custom plugins, and I have to say it really opened my eyes to some of the mysteries of OOP.

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745166458a4614660.html

相关推荐

  • php - Using register_activation_hook in classes

    I am trying to develop a plugin for basic SEO purposes, as a lot of people I know don't like using Yoast. I literal

    9小时前
    40

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信