I'm developing a custom plugin OOP based. The plugin shows up in the plugins page and it can be activated without displaying any kind of errors. But none of the functions inside seems to be working. The weird thing is that everything works like it's supposed to if I remove the class method markup.

Here's the code:

// Make sure we don't expose any info if called directly
if ( !function_exists( 'add_action' ) ) {
    echo 'Hi there!  I\'m just a plugin, not much I can do when called directly.';

 * Adds My_Custom_Plugin plugin.
if ( ! class_exists( 'My_Custom_Plugin' ) ) {

    class My_Custom_Plugin {

         * Register plugin with WordPress.
        public function __construct() {

            $this->plugin_slug  = 'My_Custom_Plugin';
            $this->version      = '1.0.0';
            $this->text_domain  = 'mcp';
            $this->plugin_dir   = basename( dirname( __FILE__ ) );

            // Localization
            load_plugin_textdomain( $this->text_domain, false, $this->plugin_dir . '/languages' );

            // Register the settings
            add_action('admin_init', array( $this, 'mcp_register_options' ) );

            // Provide a shortcut to the settings page in plugins page
            add_filter('plugin_action_links', array( $this, 'mcp_action_links'), 10, 2);

            // Display counters on Post Edit page
            add_action('admin_head-post.php', array( $this, 'mcp_display_title_counter' ) );
            add_action('admin_head-post-new.php', array( $this, 'mcp_display_title_counter' ) );
            add_action('admin_head-post.php', array( $this, 'mcp_display_excerpt_counter' ) );
            add_action('admin_head-post-new.php', array( $this, 'mcp_display_excerpt_counter' ) );


        // Define the settings
        function mcp_register_options() {

            add_settings_field( 'mcp_title_count', __('Suggested maximum amount of characters for the title', $this->text_domain), 'mcp_title_count_callback', 'writing', 'default' );
            add_settings_field( 'mcp_excerpt_count', __('Suggested maximum amount of characters for the extract', $this->text_domain), 'mcp_excerpt_count_callback', 'writing', 'default' );

            register_setting( 'writing', 'mcp_options', 'mcp_sanitize_callback' );
            register_setting( 'writing', 'mcp_title_count' );
            register_setting( 'writing', 'mcp_excerpt_count' );


        // Display and fill the form field
        function mcp_title_count_callback() {
            $mcp_title_count = esc_attr( get_option( 'mcp_title_count' ) );
            echo '<label for="mcp_title_count">
                                <input id="mcp_title_count" name="mcp_title_count" type="number" value="'. $mcp_title_count .'"/>

        function mcp_excerpt_count_callback() {

        $mcp_excerpt_count = esc_attr( get_option( 'mcp_excerpt_count' ) );
        echo '<label for="mcp_excerpt_count">
                            <input id="mcp_excerpt_count" name="mcp_excerpt_count" type="number" value="'. $mcp_excerpt_count .'"/>


        // Provide a shortcut to the settings page in plugins page
        function mcp_action_links($links, $file) {

            static $mcp;

            if (!$mcp) { $mcp = plugin_basename(__FILE__); }

            if ($file == $mcp) {
                // The "page" query string value must be equal to the slug
                // of the Settings admin page we defined earlier, which in
                // this case equals "myplugin-settings".
                $settings_link = '<a href="' . get_bloginfo('wpurl') . '/wp-admin/options-writing.php">'. __('Settings', $this->text_domain) .'</a>';
                array_unshift($links, $settings_link);

            return $links;

        // Display the Title Character Counter in the post editor
        function mcp_display_title_counter() { ?>
                jQuery(document).ready(function() {
            jQuery("#titlediv #titlewrap").before("<div style=\"line-height:24px;margin-bottom:5px;padding:0 10px;color:#666;\"><strong>Caracteres:</strong><input type=\"text\" value=\"0\" maxlength=\"3\" size=\"3\" id=\"title_counter\" readonly=\"\" style=\"width:27px;background:none;border:none;box-shadow:none;text-align:left;\"><span>(sugeridos: <?php get_option('mcp_title_count'); ?>)</span></div>");
            jQuery("#title").keyup( function() {
        <?php }

        // Display the Excerpt Character Counter in the post editor
        function mcp_display_excerpt_counter() { ?>
                jQuery(document).ready(function() {
              jQuery("#postexcerpt #excerpt").after("<div style=\" \"><strong>Caracteres:</strong><input type=\"text\" value=\"0\" maxlength=\"3\" size=\"3\" id=\"excerpt_counter\" readonly=\"\" style=\"width:35px;background:none;border:none;box-shadow:none;text-align:left;\"><span>(sugeridos: <?php get_option('mcp_excerpt_count'); ?>)</span></div>");
              jQuery("#excerpt").keyup( function() {
        <?php }

    } // class My_Custom_Plugin


If that's the entirety of plugin — you never create an object instance of your class. Class itself is merely a definition. Just like functions don't do anything unless called, definition does nothing unless object of it is created.

Thus new My_Custom_Plugin();.

Note the if you want to be a good citizen you should allow others access to that instance in some way, if they want to interact with your code for some purpose. Unfortunately WP has no established mechanisms for it and typical low effort solution is to simply store the instance in a global variable (with reasonably unique name).

| improve this answer | |
  • Thank you much, @Rarst! That was what I was missing out :) I began learning OOP plugin development directly with widgets, and somehow that line of code is not necessary for those. So today I learnt something new :P I have just one more little question... I'm getting the following error: Warning: call_user_func() expects parameter 1 to be a valid callback, function 'mcp_title_count_callback' not found or invalid function name in [...]\wp-admin\includes\template.php on line 1331. Do you have any clue what could be wrong? Everything seems to be in place :S – Rodrigo D'Agostino Apr 1 '16 at 1:59
  • How does it know it's a class method? You need to provide it same as to hooks. Overall I recommend some quality time with PHP manual. This isn't much specific to WP and learning from PHP documentation will do you more good than going by [often messed up] WP code around. – Rarst Apr 1 '16 at 8:05
  • Isn't that what the array( $this, 'funtion_name' ) sentence does? Everything's working fine now, except for that particular function along with its callbacks. What am I doing wrong here? Can you point me in the right direction with a little bit more of precision? :P – Rodrigo D'Agostino Apr 1 '16 at 9:23
  • php.net > Callbacks PS yeah, that's what it does but you seem to not use it everywhere. – Rarst Apr 1 '16 at 9:48
  • Holly Molly! That last thing you say was the right pointer for me to understand what you meant in the beginning. I needed to use the same structure for the callback parameter. It really makes sense now xP I really appreciate that you took the time to make me understand, @Rarst. Your teaching method can be a little annoying, but I prefer it that way ;) Thanks a bunch, mate! – Rodrigo D'Agostino Apr 1 '16 at 18:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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