Starter-Kit is a development kit for rapidly building WordPress plugins, providing some useful services and a clean way to structure a plugin.
- Starter-Kit now makes use of namespaces and reflection available in PHP 5.3 or better.
- Streamlined Feature-based development process ("Drop-ins" are out ).
- Classes extending Feature no longer need the init function.
- All basic Starter-Kit behavior has been moved to separate classes and can be removed from the plugin if not needed.
- Template and option services live in the
Feature
class/subclasses. - Nearly all boilerplate has been moved into the
Kit
orFeature
classes and out of the way. - Added Grunt support
- SASS, autoprefixer and cssmin for CSS
- JavaScript inspected JSHint and compressed with Uglify
- Images compressed with imagemin
- Default grunt task and watch task
dev
- npm install from package.json
I can't speak for other developers, but when I build a plugin I want to think about what the plugin does, the features I plan to build. I want to use OOP. I want a clean separation of my PHP logic and HTML views. I want to start a project running with everything I need -- then I never want to see it, much less think about it, again. That's what this framework is designed to provide.
With Starter-Kit, you build Features -- small classes that extend from the framework's Feature
class -- and drop them into one of three folders (admin
, client
, or core
). These files are automatically loaded
in the correct context so you're only loading the functionality you need, when you need it.
Your classes aren't required to extend Feature, but doing so adds some useful services:
- Auto-initializing static method for instantiating your feature from an action-hook.
- A template class for separating your class's logic from HTML output.
- WordPress-options handling methods, with values namespaced to your plugin.
- Class-Friendly methods for adding actions & filters to WordPress without array-wrapping.
- WordPress AJAX methods wrapped for easy implementation.
In addition to auto-class loading, the base plugin itself:
- handles default plugin options, with the ability to handle defaults between plugin versions.
- adds an init hook for your plugin that triggers Feature initiation.
- sets default template directory for your rendering
- provides ready for use activation and deactivation hooks
And if that's not enough for you, the project comes ready for use with SASS, autoprefixer, jshint, uglify and imagemin -- and of course it's just a starting point. Grunt being grunt means you can tweak it to your heart's content.
Starter-Kit is a template that you customize:
- Download Starter-Kit and copy the files to your development directory and rename 'starter-kit.php' to the name of your plugin.
- In your development tool of choice, do a global find-and-replace:
- Replace all instances of the "irresponsible_art\starter_kit" namespace to reflect your organization
- Replace all instances of "StarterKit" to your class name.
- Replace all instances of "starter_kit" with your plugin's slug -- this will replace things like the WP text-domain, options key, and a few other strings.
If you plan to use grunt in your project:
- Open
package.json
and change the name and version of the plugin. - In a console, navigate to your development directory and run 'npm install'.
- In that same console, run 'grunt' to test your build
- When you're ready to work, run the 'grunt dev' to start the watch task ( ctrl + C stops it ).
The framework comes with a sample feature already in the assets/core folder ("rebrand.php") which rebrands the WordPress login screen and utilizes the majority of the frameworks features, if only in a basic way.
The examples below are all pulled from the Rebrand class. Keep in mind that the examples do not change the default namespace or plugin slug -- but you must.
The "rebrand" feature is not only one of the stupidest examples of a plugin known to man, it also goes to near idiotic lengths to make use of SASS, JavaScript, templates, actions, filters and options. No one in their right mind would use it.
That said, the animation is still kind cool to watch.
Features are classes that extend Starter-Kit's Feature class, inheriting useful methods for interacting with WordPress, rendering HTML and initialization. Here's a minimal example:
namespace irresponsible_art\starter_kit;
class LoginBranding extends Feature {
public function __construct() {
...
}
}
// Subscribe to the drop-in to the initialization event
add_action( 'starter_kit_init', array( 'irresponsible_art\starter_kit\LoginBranding', 'init' ) );
Again, you'd need to replace "irresponsible_art\starter_kit" namespace at the top and bottom of the document, as well as changing 'starter_kit_init' to use your slug instead of "starter_kit".
Your feature should then add actions or filters in the constructor and handle them in the body of the class. The Feature class exposes three methods for making handling actions & filter methods.
$this->marshal( $methodName )
- Creates a
callable
array for the method corresponding to the string$methodName
. $this->add_action( $action, $method_name, $priority = 10, $accepted_args = 2 )
- Marshaled version of WordPress add_action method
$this->add_filter( $action, $method_name, $priority = 10, $accepted_args = 2 )
- Marshaled version of WordPress add_filter method
The Rebrand feature adds a filter for login_message
and the method to handle the filter:
namespace irresponsible_art\starter_kit;
class LoginBranding extends Feature {
public function __construct() {
$this->add_filter( 'login_message', 'plugin_login_message' );
}
public function plugin_login_message( $message ) {
// Handle the filter here.
return $message ;
}
}
// Subscribe to the drop-in to the initialization event
add_action( 'starter_kit_init', array( 'irresponsible_art\starter_kit\LoginBranding', 'init' ) );
The rebrand feature stores the name of the plugin as a WordPress option, and Starter-Kit stores
options within a plugin namespace -- in this case a single variable in the WordPress options. But
you don't have to worry about any of that. Just use the get_option
method.
namespace irresponsible_art\starter_kit;
class LoginBranding extends Feature {
public function __construct() {
$this->add_filter( 'login_message', 'plugin_login_message' );
}
public function plugin_login_message( $message ) {
$plugin_name = $this->get_option( 'plugin_name', 'Starter Kit!' ) ;
// Do something with $plugin_name....
return $message ;
}
}
// Subscribe to the drop-in to the initialization event
add_action( 'starter_kit_init', array( 'irresponsible_art\starter_kit\LoginBranding', 'init' ) );
The get_option
method takes 2 parameters; The name of the option and a fallback value -- so you
never get back a null or empty value from WordPress (unless you want to).
Another way of dealing with unset values is the defaults
method in the core plugin file. This method
contains an array of option values your plugin needs and saves them all when the plugin is activated.
This ensures that you always have meaningful values when the user installs the plugin.
You can also update the defaults array as your plugin grows. Each time the plugin is activated (such as when a new version is installed) your plugin will update the options, removing unused values, adding new values, and preserving existing ones.
The rebrand feature would update the defaults array:
private static function defaults() {
return array(
'plugin_name' => 'Starter Kit'
);
}
The Rebrand feature should output the string "$plugin_name Activate!" in an HTML formatted message. We could use inline strings to output the message (and in reality, you probably should with an HTML string this simple), but Starter-Kit attempts to separate HTML rendering from PHP logic by using templates.
namespace irresponsible_art\starter_kit;
class LoginBranding extends Feature {
public function __construct() {
$this->add_filter( 'login_message', 'plugin_login_message' );
}
public function plugin_login_message( $message ) {
$plugin_name = $this->get_option( 'plugin_name', 'Starter Kit!' ) ;
$template = $this->get_template();
$template->set( 'plugin_name', $plugin_name );
$message = $template->apply( 'login-message.php' );
return $message ;
}
}
// Subscribe to the drop-in to the initialization event
add_action( 'starter_kit_init', array( 'irresponsible_art\starter_kit\LoginBranding', 'init' ) );
Calling the Feature class' get_template()
method returns a shiny new Template
class for your string output.
The Template
class is very simple, having only a few methods:
$template->set( $name, $value )
- Sets a variable in the template scope with the name
$name
and the value$value
. $template->set_vars( $pairs, $append = TRUE )
- Sets an array of name/value pairs -- Same as calling
set()
multiple times. The second optional parameter can be set to `FALSE` if you want to remove any previously set values. $template->clear()
- Clears any values previously set with
set()
orset_vars()
apply( $file, $use_default_path = TRUE )
- Applies the previously set values to the template with the name in `$file` and returns the rendered string.
Template files are assumed to be in the
assets/templates
folder. If your template is in another location, specify the full path in the$file
value and use the optional second parameter, setting it toFALSE
Template files themselves are PHP -- no confusing syntax or regex/string replacement to slow down your plugin.
<div class="message hidden"><?php echo $plugin_name ; ?> has been activated</div>
The plugin exposes two additional method not used in the Rebrand Feature.
$this->add_admin_ajax( $action )
- Using the method provided in
$action
, creates an WordPress AJAX action of the same name. This action will only be available to users who are logged into WordPress. $this->add_client_ajax( $action )
- Using the method provided
$action
, creates an WordPress AJAX action of the same name. This action will be available to all users.
Like the action / filter hooks, the ajax methods attach themselves to methods in your Feature
subclasses.
The method names become the "actions" that WordPress recognizes when an AJAX request is made and routes the
request to your class' method. A quick-and-dirty example:
The Feature class:
namespace irresponsible_art\starter_kit;
class BasicMath extends Feature {
public function __construct() {
$this->add_admin_ajax( 'add_numbers' );
}
public function add_numbers(){
$num_1 = intval( $_POST[ 'num_1' ] );
$num_2 = intval( $_POST[ 'num_2' ] );
echo $num_1 + $num_2 ;
die( ) ;
}
}
// Subscribe to the drop-in to the initialization event
add_action( 'starter_kit_init', array( 'irresponsible_art\starter_kit\BasicMath', 'init' ) );
The JavaScript, somewhere in our page:
jQuery( document ).ready( function( $ ) {
var data = {
action: 'add_numbers',
num_1: 2,
num_1: 3
};
$.post( ajaxurl, data, function( response ) {
alert( 'Got this from the server: ' + response );
});
});
AJAX in WordPress is a fairly large topic. For more information on using AJAX with WordPress, see the codex article AJAX in Plugins.
Starter-Kit now comes with a very basic grunt configuration. The setup is fairly simple:
- grunt tasks are in the root of the
grunt
folder. - grunt task options are in the
grunt/options
folder.
The structure is fairly flexible, and you should be able to add additional tasks and options or edit the existing elements at will. But the nuts and bolts of using grunt is far beyond the scope of this readme. The best tutorial (IMO) is from Chris Coyier on 24 Ways. You can also view his screen cast on the subject.
JavaScript goes in the in the assets/js
folder. The grunt task will lint (that means, check your code quality
using jshint) and then uglify ( that means, make it really really tiny ) the JavaScript files in this
directory, with a few exceptions:
- Files with the extension '.min.js' are assumed to be uglified already, and are ignored.
- Files that begin with an underscore ('_') are assumed to be included in some other way and are ignored.
- Any files in a subdirectory of
assets/js
are assumed to be included in some other way and are ignored.
Starter-Kit uses SASS (.scss) and these files live in the in the assets/css
folder.
The grunt task will compile the sass files, run autoprefixer,
and minifiy the resulting css file. Grunt will ignore:
- Files with the extension '.css' - assumed to have already been processed.
- Files that begin with an underscore ('_') are assumed to have been included using @import.
- Any files in a subdirectory of
assets/css
are assumed to have been included using @import.
The grunt task will minify all images placed in the assets/img
folder using imagemin. Don't worry,
imagemin is smart enough to know what images have been previously minified.
The default task, that is, just typing 'grunt' into the command line while in your plugin folder, will perform all of the above actions.
However, the 'dev' ( that is, typing 'grunt dev' ) will start the watch task. This task will monitor the files in
your development directory, waiting for changes. So when you edit a JavaScript file in the assets/js
folder
the dev task will see the change and run the appropriate tasks (jshint and uglify), even if that file is in a subfolder.
The same goes for the editing an .scss file or adding an image to the assets/img
folder.
grunt creates a few temporary folders -- be sure to avoid uploading these to your server or including them in SVN (if you add them to wordpress.org/plugins). These folders are:
.sass-cache
- created when compiling sass.tmp
- created when running autoprefixer and cssminnode_modules
- contains code necessary to run grunt and grun tasks
I should mention that this is a living codebase -- I use this any time I'm building plugin. As a result, the code changes frequently. I attempt to keep the code updated (mostly happens) as well as the documentation (mostly doesn't happen). Feel free to submit issues, or browse the issues list to see what other features are planned.
Thanks for playing.