How to structure a plugin

This isn’t a question about how to build a WordPress plugin. Rather, what, if any, guides could be applied to how to put together the file architecture of any plugin.

Some other programming languages or libraries have very controlled ways of organizing directories and files. Sometimes this is annoying and highlights the freedom that PHP offers, but on the flip-side WordPress plugins are put together in any fashion as determined by their author.

Read More

There isn’t a right answer, but my hope is to refine how I, and others, build plugins to make them more friendly for other developers to disect, easier to debug, easier to navigate, and possibly more efficient.

The final question: what do you think is the best way to organize a plugin?

Below are a few sample structures, but in no way is an exhaustive list. Feel free to add your own recommendations.

Assumed Default Structure

  • /wp-content
    • /plugins
      • /my-plugin
        • my-plugin.php

Model View Controller (MVC) method

  • /wp-content
    • /plugins
      • /my-plugin
        • /controller
          • Controller.php
        • /model
          • Model.php
        • /view
          • view.php
        • my-plugin.php

MVC’s three parts:

  • The model interacts with the database, querying and saving data, and contains logic.
  • The controller would contain template tags and functions that the view would utilize.
  • The view is responsible to display the data provided by the model as constructed by the controller.

Organized by type method

  • /wp-content
  • /plugins
    • /my-plugin
      • /admin
        • admin.php
      • /assets
        • css/
        • images/
      • /classes
        • my-class.php
      • /lang
        • my-es_ES.mo
      • /templates
        • my-template.php
      • /widgets
        • my-widget.php
      • my-plugin.php

WordPress Plugin Boilerplate

Available on Github

Based on the Plugin API, Coding Standards, and Documentation Standards.

  • /wp-content
    • /plugins
      • /my-plugin
        • /admin
          • /css
          • /js
          • /partials
          • my-plugin-admin.php
        • /includes
          • my_plugin_activator.php
          • my_plugin_deactivator.php
          • my_plugin_i18n.php
          • my_plugin_loader.php
          • my_plugin.php
        • /languages
          • my_plugin.pot
        • /public
          • /css
          • /js
          • /partials
          • my-plugin-public.php
        • LICENSE.txt
        • README.txt
        • index.php
        • my-plugin.php
        • uninstall.php

Loosely organized method

  • /wp-content
  • /plugins
    • /my-plugin
      • css/
      • images/
      • js/
      • my-admin.php
      • my-class.php
      • my-template.php
      • my-widget.php
      • my-plugin.php

Related posts

Leave a Reply

10 comments

  1. Note that plugins are all “controllers” by WP standards.

    It depends on what the plugin is supposed to do, but in all cases I would try to separate the screen output from the PHP code as much as possible.

    Here’s one way to do that easily – first, define a function that loads the template:

    function my_plugin_load_template(array $_vars){
    
      // you cannot let locate_template to load your template
      // because WP devs made sure you can't pass
      // variables to your template :(
      $_template = locate_template('my_plugin', false, false);
    
      // use the default one if the theme doesn't have it
      if(!_$template)
        $_template = 'views/template.php';
    
      // load it
      extract($_vars);        
      require $template;
    }
    

    Now, if the plugin uses a widget to display data:

    class Your_Widget extends WP_Widget{
    
      ...      
      public function widget($args, $instance){
    
        $title = apply_filters('widget_title', $instance['title'], $instance, $this->id_base);
    
        // this widget shows the last 5 "movies"
        $posts = new WP_Query(array('posts_per_page' => 5, 'post_type' => 'movie')); 
    
        if($title)
          print $before_title . $title . $after_title;
    
        // here we rely on the template to display the data on the screen
        my_plugin_load_template(array(
    
          // variables you wish to expose in the template
         'posts'    => $posts,          
        ));
    
        print $before_widget;
      }
      ...
    
    }
    

    The template:

    <?php while($posts->have_posts()): $posts->the_post(); ?>
    
    <p><?php the_title(); ?></p> 
    
    <?php endwhile; ?>
    

    Files:

    /plugins/my_plugin/plugin.php           <-- just hooks 
    /plugins/my_plugin/widget.php           <-- widget class, if you have a widget
    /themes/twentyten/my_plugin.php         <-- template
    /plugins/my_plugin/views/template.php   <-- fallback template
    

    Where do you put your CSS, JS, images, or how do you design the container for the hooks is less important. It’s a matter of personal preference I guess.

  2. It depends on the plugin. This is my basic structure for nearly every plugin:

    my-plugin/
        inc/
            Any additional plugin-specific PHP files go here
        lib/
            Library classes, css, js, and other files that I use with many
            plugins go here
        css/
        js/
        images/
        lang/
            Translation files
        my-plugin.php
        readme.txt
    

    This would be something that would go in the lib folder.

    If it’s particularly complex plugin, with a lot of admin area functionality, I’d add an admin folder to contain all of those PHP files. If the plugin does something like replace included theme files, there maybe a template or theme folder as well.

    So, a directory structure might look like this:

    my-plugin/
        inc/
        lib/
        admin/
        templates/
        css/
        js/
        images/
        lang/
        my-plugin.php
        readme.txt
    
  3. IMHO, the easiest, most powerful, and most maintainable route is to use an MVC structure, and WP MVC is designed to make writing MVC plugins very easy (I’m a little biased, though…). With WP MVC, you simply make the models, views, and controllers, and everything else is handled behind the scenes for you.

    Separate controllers and views can be made for the public and admin sections, and the entire framework takes advantage of many of WordPress’s native features. The file structure and much of the functionality is exactly the same as it is in the most popular MVC frameworks (Rails, CakePHP, etc).

    More info and a tutorial can be found here:

  4. We’re using a mix of all methods. First of all, we’re using the Zend Framework 1.11 in our plugins and therefore we had to use a similar structure for the class files because of the autoload mechanic.

    The structure of our core plugin (which is used by all our plugins as a base) looks similar to this:

    webeo-core/
        css/
        images/
        js/
        languages/
        lib/
            Webeo/
                Core.php
            Zend/
                /** ZF files **/
            Loader.php
        views/
        readme.txt
        uninstall.php
        webeo-core.php
    
    1. WordPress calls the webeo-core.php file in the plugin root folder.
    2. In this file we’re going to set the PHP include path and register the activation and deactivation hooks for the plugin.
    3. We also have a Webeo_CoreLoader class inside this file, which sets some plugin constants, initialize the class autoloader and make a call to the setup method of the Core.php class inside the lib/Webeo folder. This runs on the plugins_loaded action hook with a priority of 9.
    4. The Core.php class is our plugin bootstrap file. The name is based on the plugins name.

    As you can see, we have a subdirectory inside the lib folder for all our vendor packages (Webeo, Zend). All sub packages inside a vendor are structure by the module itself. For a new Mail Settings admin form, we would have the following structure:

    webeo-core/
        ...
        lib/
            Webeo/
                Form/
                    Admin/
                        MailSettings.php
                    Admin.php
                Core.php
                Form.php
    

    Our sub-plugins have the same structure with one exception. We go a level deeper inside the vendor folder due to resolve naming conflicts during the autoload event. We also call the plugins boostrap class E.g. Faq.php on priority 10 inside the plugins_loaded hook.

    webeo-faq/ (uses/extends webeo-core)
        css/
        images/
        js/
        languages/
        lib/
            Webeo/
                Faq/
                    Faq.php
                    /** all plugin relevant class files **/
        views/
        readme.txt
        uninstall.php
        webeo-faq.php
    

    I will probably rename the lib folder to vendors and move all public folders (css, images, js, languages) to a folder named public in the next release.

  5. Like many here already answered It really depends on what the plugin is supposed to do, but here is my base structure:

    my-plugin/
        admin/
            holds all back-end administrative files
            js/
                holds all back-end JavaScript files
            css/                    
                holds all back-end CSS files
            images/
                holds all back-end images
            admin_file_1.php        back-end functionality file
            admin_file_2.php        another back-end functionality file 
        js/
            holds all front end JavaScript files
        css/
            holds all fronted CSS files
        inc/
            holds all helper classes
        lang/                   
            holds all translation files
        images/
            holds all fronted images
        my-plugin.php               main plugin file with plugin meta, mostly includes,action and filter hooks
        readme.txt                  
        changelog.txt
        license.txt
    
  6. I am partial to the following plugin layout, however it usually changes depending on what the plugin requirements are.

    wp-content/
        plugins/
            my-plugin/
                inc/
                    Specific files for only this plugin
                    admin/ 
                        Files for dealing with administrative tasks
                lib/
                    Library/helper classes go here
                css/
                    CSS files for the plugin
                js/
                    JS files
                images/
                    Images for my plugin
                lang/
                    Translation files
            plugin.php 
                This is the main file that calls/includes other files 
            README 
                I normally put the license details in here in addition to helpful information 
    

    I have yet to create a WordPress plugin requiring a MVC style architecture but if I was to do this I would lay it out with a separate MVC directory, which itself contains views/controllers/models.

  7. All my plugins follow this structure, which seems to be very similar to what most other devs are doing:

    plugin-folder/
        admin/
            css/
                images/
            js/
        core/
        css/
            images/
        js/
        languages/
        library/
        templates/
        plugin-folder.php
        readme.txt
        changelog.txt
        license.txt
    

    plugin-folder.php is then usually a class that loads all required files from the core/ folder. Most often on the init or plugins_loaded hook.

    I used to prefix all my files as well, but as @kaiser noted above, it’s really redundant and I’ve recently decided to remove it from any future plugins.

    The library/ folder holds all external helper libraries that the plugin might depend on.

    Depending on the plugin, there might be an uninstall.php file in the plugin root as well. Most of the time this is being handled via register_uninstall_hook(), though.

    Obviously, some plugins might not require any admin files or templates, etc, but the structure above works for me. In the end you just have to find a structure that works for you and then stick with it.

    I also have a starter plugin, based on the structure above that I use as a starting point for all my plugins. All I need to do then is do a search/replace for function/class prefixes and off I go. When I was still prefixing my files that was an extra step I had to do (and quite annoying at that), but now I just have to rename the plugin folder and the main plugin file.

  8. My logic, the bigger the plugin the more structure I use.
    For big plugins I tend to use MVC.
    I use this as a starting point and skip what isn’t needed.

    controller/
        frontend.php
        wp-admin.php
        widget1.php
        widget2.php
    model/
        standard-wp-tables.php // if needed split it up
        custom-tabel1.php
        custom-tabel2.php
    view/
        helper.php
        frontend/
            files...php
        wp-admin/
            files...php
        widget1/
            file...php
        widget2/
            file...php
    css/
    js/
    image/
    library/  //php only, mostly for Zend Framework, again if needed
    constants.php //tend to use it often
    plugin.php //init file
    install-unistall.php  //only on big plugins
    
  9. A less common approach to structuring a plugin’s files and directories is the file type approach. It’s worth mentioning here for completeness:

    plugin-name/
        js/
            sparkle.js
            shake.js
        css/
            style.css
        scss/
            header.scss
            footer.scss
        php/
            class.php
            functions.php
        plugin-name.php
        uninstall.php
        readme.txt
    

    Each directory contains files of that type only. It’s worth noting that this approach falls short when you have many file types .png .gif .jpg that might be more logically filed under a single directory, images/ for example.