WordPress Plugin Boilerplate, featuring automated tests, React, Composer, PHP Namespaces, and the WordPress Customizer.
- The Boilerplate is based on the Plugin API, Coding Standards, and Documentation Standards.
- All classes, functions, and variables are documented so that you know what you need to change.
- The Boilerplate uses a strict file organization scheme that corresponds both to the WordPress Plugin Repository structure, and that makes it easy to organize the files that compose the plugin.
- The project includes a
.pot
file as a starting point for internationalization.
- WPBrowser (Codeception) tests are already setup, run on GitHub Actions, and are easy to use! See the
tests/tests.md
file for more details. - Built in a way that isn't just a skeleton (blank slate) but, instead, a fully-functioning plugin out of the box. We're of the opinion that it's quicker to dev and easier to learn if you can just delete what you don't need and rework existing code into what you do need.
- Thorough code documentation with regular updates.
- Adds a wp-admin Settings page with React components and the Settings API (restricts to Administrators). Bonus: legacy code that direct-links to the plugin's options in the WordPress Customizer (so use one or both for your plugin's options).
- Tailwind CSS framework can be used anywhere and is optimized so all classes are available but only the used ones get built into the final CSS.
- Plugin assets (CSS and JS) are served minified with external sourcemaps, but unminified files exist as well for when
SCRIPT_DEBUG
istrue
or you're running Parcel's watching / Hot Module Replacement (HMR). - Displays a wp-admin error notice to administrators if the required version of PHP is not met, saving users from a fatal error.
- Easily add a new shortcode by extending the abstract
Shortcode
class and adding to the array of shortcodes in theManage_Shortcodes
class. - Primarily relies upon Composer and Parcel to build the plugin and make some complex stuff pretty simple to get up and running quickly.
- Displays a wp-admin error notice to administrators if a required third-party plugin (e.g. WooCommerce) is not active.
- Includes a number of generally-helpful utility functions, such as getting all public post types, flattening an array of unknown dimensions, and sane option setters and getters.
- Uses Composer to zip the files for public distribution as an installable plugin, making sure to exclude build files and directories.
The Boilerplate can be installed directly into your plugins folder "as-is". You will want to rename it and the classes inside of it to fit your needs.
Currently a lot of Find and Replace, but you're welcome to contribute some automation to improve the initial setup.
- Copy this cliff-wp-plugin-boilerplate repository/directory to your wp-content/plugins directory and rename your new plugin's directory
- Delete the
.github/FUNDING.yml
file - Delete the
.all-contributorsrc
file - Perform a case-sensitive search and replace at the project level, as follows:
- Rename the
cliff-wp-plugin-boilerplate
directory toyour-plugin-name
. This is your new plugin directory and must match your text domain. - Find the text
cliff-wp-plugin-boilerplate
and replace withyour-plugin-name
in all files (will be your Text Domain) - Find the text
cliff_wp_plugin_boilerplate
and replace withyour_plugin_name
in all files (must match the above, just with underscores) - Find the text
WordPress Plugin Boilerplate
and replace withYour Plugin Name
in all files - Find the text
WpPluginName
and replace withYourPluginName
in all files (the namespace) - Rename the
pot
file underlanguages
and replace the stringcliff-wp-plugin-boilerplate
withyour-plugin-name
- Find the text
https://www.example.com/
and replace with your URI in all files - Find the text
Your Name or Your Company
and replace with your name in all files - Find the text
your@email.address
and replace with your email address incomposer.json
- Find the text
cliffpaulick
and replace with your WordPress.org username (or delete it) inreadme.txt
- Find the text
yourname
and replace with whatever you want as your vendor name) incomposer.json
(such as your GitHub username) - Make other edits to
readme.txt
as appropriate for your own plugin
- Rename the
- Make sure everything in
composer.json
is appropriate to your project.- You do not need
tgmpa/tgm-plugin-activation
if your plugin does not require or recommend any other plugins or themes. - Make sure to update the main plugin file's logic accordingly if you fully remove this library.
- Make sure to update the main plugin file's class properties:
$min_php
$required_theme
$required_plugins
- You do not need
- Go through all your PHP and JSON files to make sure your plugin descriptions are set.
- Make sure everything in
package.json
is also appropriate to your project. - Run
composer install
- Run
npm update
(we purposefully don't commit package or composer lock files in the boilerplate, but you should in your repo) - Run
npm install
- Run
npm run start
if actively working your CSS or JS (to get HMR), elsenpm run build
- Activate the plugin
- Check if everything's working as it should (that it can be activated and without any errors)
- If it works (as it should), delete THIS README.md FILE
- You need to install
tric
globally - So that you can run
tric composer install
andtric composer update
commands, ESPECIALLY not running Composer's update command outside of tric, or else your GitHub Actions will likely fail due to not finding a set of installable components. - You should also install NVM and keep the
.nvmrc
file updated as you decide is appropriate.
Visit https://getcomposer.org/ to learn all about it.
Here are some quick notes about Composer, in general, and this project's use of it:
- You need to install Composer on your desktop/laptop, not your server. You can download it right into your
cliff-wp-plugin-boilerplate
directory. - The
composer.json
file is the instructions file that tells thecomposer.phar
how to build yourvendor
directory (which includes the autoloader), and possibly do other things. - Run
composer install
to generate yourcomposer.lock
file. - Because
composer.json
has"optimize-autoloader": true
inside the config key, you will need to run Composer'supdate
if you ever add a new PHP class- See https://getcomposer.org/doc/articles/autoloader-optimization.md for more details.
- It is set this way to lean toward distribution convenience more than development convenience.
- Make sure to have npm installed on your computer.
- Open your plugin folder in your Terminal.
- Run
npm install
so node_modules gets installed. - Run
npm run start
to get everything built and up-and-running, including Parcel's HMR.- Ctrl + C to kill the Parcel watcher.
- Activate your plugin and see your Admin area has noticeably dumb styles (like all links as green) and JavaScript alert() noise. This is to confirm Parcel is running successfully and to annoy you so you get started on your customizations. ;)
- If you don't see the alerts, check your console. It could be that Parcel's HMR is disallowed by your browser because it's HTTP (if your localhost is HTTPS). In this case, click the
wss://...
to open in a new tab, it won't load, change it tohttps://...
and your browser will complain because there's no valid cert. Just add the exception and then you won't have to do this again unless you delete the.cache
directory created by Parcel. 3 minute demo of these steps - If you're on HTTP and not seeing the alerts, an unknown issue is the cause.
- If you don't see the alerts, check your console. It could be that Parcel's HMR is disallowed by your browser because it's HTTP (if your localhost is HTTPS). In this case, click the
- Once your PHP, CSS, and JS coding is complete:
- If you're still running Parcel's watcher, kill it.
- Run
npm run zip
to build your installable/distributable plugin.
- As stated above, run
npm run zip
. - Composer will create the .zip right in the project's directory, only after first running production build and make-pot commands.
- Unzip this newly-created
cliff-wp-plugin-boilerplate.zip
file to make sure it got built correctly (excluding files like.gitignore
,composer.json
,package.json
, etc). - Upload this .zip to your production site or wherever you want to distribute it.
- Delete this .zip file from your hard drive.
Following is the pre-built plugin structure. You can add your own new class files (include namespace
and use
at the top) by naming them correctly and putting the files in the most appropriate location.
cliff-wp-plugin-boilerplate/src/Admin
- admin-specific functionalitycliff-wp-plugin-boilerplate/src/Common
- functionality shared between the admin area and the public-facing partscliff-wp-plugin-boilerplate/src/Core
- plugin core to register hooks, load files etccliff-wp-plugin-boilerplate/src/Customizer
- WordPress Customizer functionalitycliff-wp-plugin-boilerplate/src/Frontend
- public-facing functionalitycliff-wp-plugin-boilerplate/src/Shortcodes
- create and enable/disable new shortcodescliff-wp-plugin-boilerplate/tests
- all the tests
This plugin requires PHP 7.1.0 or newer and will display a wp-admin error notice if activated in an environment that does not meet this or other requirements (such as required plugins or other dependencies you may code).
You can see the current WordPress usage of each PHP version at https://wordpress.org/about/stats/. A requirement of 7.1+ meets 74.2% of all WordPress installs as of December 17, 2020. Most of those not using PHP 7.1+ are assumed to be inactive sites.
Your requiring a PHP version update for anyone who might want to use your plugin will actually benefit them long-term, as their site will be quicker, more secure, and ready for future version bumps. In fact, WordPress already recommends using PHP 7.4+ and has an "Update PHP" help article.
For each new version, before running the zip process, don't forget to:
- Add a changelog entry to
readme.txt
- Update the version number in your...
readme.txt
file's headerpackage.json
file- main plugin file's header
- main plugin file's
PLUGIN_VERSION
constant
We do not use a variable for strings' text domain because it does not work when using the WP CLI command, nor WordPress.org. Reference
The Composer archive command runs the asset build command, then the WP CLI make-pot command, ...but make sure to customize the make-pot command's arguments in the package.json script.
This WordPress Plugin Boilerplate is licensed under GPL version 3 or any later version.
This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 3 or any later version, as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
A copy of the GNU General Public License should be included in the root of this plugin's directory. The file is named
license.txt
; if not, obtain one before using this software by visiting https://www.gnu.org/licenses/gpl-3.0.html or writing to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
If you opt to use third-party code that is not compatible with this software's license, then you may need to switch to using code that is compatible.
As an example, here's a discussion that states GPLv2-only plugins could not bundle work licensed with Apache 2.0.
The WordPress Plugin Boilerplate was started in 2011, by Tom McFarlin and has since included a number of great contributions. In March of 2015, the project was handed over by Tom to Devin Vinson.
This plugin boilerplate was created by Clifford Paulick in 2018, as a fork of WordPress Plugin Boilerplate with Namespace and Autoloader Support, which forked the WordPress Plugin Boilerplate project to add support for Composer (including autoloading) and namespaces.
Reporting issues -- and especially submitting Pull Requests -- are welcome! Do not contribute if you do not agree to this software's license terms.
Documenting this project's progress...
- Updated - Instructions and commands for generating the zip, now that
tric
is in the mix. - Updated - NPM package versions.
- Updated - WordPress' React Button components to replace
isDefault
withisSecondary
and removeisLarge
and addisSmall
on buttons that were notisLarge
.
- Added - WPBrowser (Codeception) tests that run via tric, just covering some of the Utilities classes as a start.
- Updated - Corrected or enhanced various Utilities functions, thanks to implementing passing tests.
- Updated - Moved
current_request_is()
from Common to Http.
- Added CONTRIBUTING and CODE_OF_CONDUCT documentation files to the repository.
- Implement Tailwind CSS, which gives us thousands of possible class names to use throughout for layouts, colors, sizing, borders, transforms, responsive, and more.
- Tailwind was chosen because it fit nicely into our existing PostCSS build process, has healthy community involvement, is infinitely customizable (custom colors, breakpoints, prefixing, fonts, etc.), and keeps us in our JS building stuff without having to fight against opinionated/bundled components. What is Tailwind?
- By itself, Tailwind is about 0.6MB minified out of the box so we now also run PurgeCSS so only the classes from Tailwind that you use will make it into your generated CSS.
- Therefore, this boilerplate's minified admin-settings.css (where all the React, JS, and CSS is) ends up around 12KB (0.01MB).
- This is great, except you'll need to make sure you whitelist external classes, such as from WordPress (this is a start but not comprehensive).
- This also means you'll need to add/remove classes in your JS instead of your browser inspector because that Tailwind class actually doesn't exist in your CSS unless it's used in your JS.
- Yes, it works with HMR. Just add that additional class to your React component and see if it's just what you wanted in an instant.
- Remove sourcemaps from the unminified build because they just pointed to the raw PostCSS, which isn't helpful when we're trying to figure out how it all compiled down into actual CSS (or JS). Plus, overall unzipped file size reduced over 100KB.
- The unminified files only load if
SCRIPT_DEBUG
is enabled. - The sourcemaps are still available if loading the minified files.
- The unminified files only load if
- Remove the JavaScript
alert()
from Common, Admin, and Frontend.
- Admin Settings page: Add an example multi-select option (demo GIF), requiring quite the multidimensional array to get registered as a Setting: "show_in_rest" > "schema" > "items" (type=array) > "items" (type=integer)
- Add
declare( strict_types=1 );
to the top of all PHP files. - Admin Settings page: Make one of the default buttons link to our own Customizer panel.
- Admin Settings page: Add tabbed navigation with icons in the tab names and styling to support the wp-admin color schemes. 2 minute demo video
- Change the Strings utility class to implement the voku/stringy library.
- Change the way Assets (CSS/JS) are handled, making them have to be registered before enqueued (best practice) and make it easier to do so for our internal assets, only needing the file name from the /dist folder.
- Add more reliable detection of when is a frontend request--example:
Common::current_request_is( 'frontend' )
--to improve performance, additionally being able to detect more things: REST API, Ajax, or WP-Cron requests. - Add "build:pot" npm command that gets ran upon Composer archive.
- Rename files, folders, class names, and namespaces to be PSR-4 compatible to avoid deprecation notices as of Composer 1.10.0 (March 10, 2020).
- Improve the JavaScript build for WordPress React, reducing the
admin-settings.js
file size:- Minified: from 265.91 KB to 35.16 KB (87% reduction)
- Unminified: from 803.68 KB to 48.15 KB (94% reduction)
- Improve the PostCSS build process to disable modules (rewriting selectors), enable writing nested CSS, and enable variables.
- Admin Settings page:
- Protect components that get disabled while saving from getting permanently disabled if the API response never comes back (such as if PHP terminates).
- Force displaying an error notification even if the API response was technically successful but isn't really due to a
null
response. - Fix the example radio button's validation logic in
register_setting()
by adding the correct "show_in_rest" > "schema" > "enum" args, removing the "sanitize_callback" arg, and using the "rest_api_init" hook. - Added basic styling.
- Changed plugin text domains from variable to string to allow using WP CLI command and be compliant with WordPress.org out of the box.
- Rebuild admin Settings Page:
- Rebuilt via React and JSX.
- Requires React version 16.8+ to be able to use Hooks, which means we now require WordPress version 5.2+ (from May 7, 2019)
- Only loads CSS/JS for the Settings Page when we're on the Settings Page.
- Added a heading area for things like your logo and button links.
- Added a few demo settings fields (like toggle) to help get up and running quickly.
- Hot Module Replacement (HMR) is now working when the plugin is active on a localhost WordPress installation. This is awesome because, for example, you could edit your PCSS from
p { color: blue; }
top { color: green; }
and your text will be green-colored before you can even switch back from your code editor to your web browser! (Yes, it works for JavaScript, too.) - Add easy asset handle maker to keep styles and scripts named consistently but uniquely. Example:
Plugin_Data::get_asset_handle( 'admin-settings' )
Loader()
itself now fires on'init'
priority3
instead of the default10
so that we can add our ownadd_action( 'init', ... );
without needing to also pass a priority greater than10
.- Add
Http
utility class, becoming a helper to get$_REQUEST
values without needing to use the[tk_request]
example shortcode (so just delete it unless you need it).
- Rework the Post Utility class'
post_id_helper()
to be simplified as well as accepting a Post Type filter. - Enhance the abstract shortcode class to automatically register each shortcode with Toolset Views (enabled/disable per shortcode).
- Fix an array/string type error in the abstract shortcode class.
- Running Composer's archive command now does an npm build for production to ensure we've got the latest-greatest.
- Fix the build process (JS and CSS) so unminified files get shipped so they can be loaded per the SCRIPT_DEBUG constant, according to WordPress best practices.
- Entirely change the build process (from Gulp+Sass to Parcel+PostCSS) for simplicity, many small gains (Hot Module Replacement + you can still use Sass instead of or in addition to PostCSS), and a more flexible foundation going forward.
- Note that Parcel's PostCSS does nothing more than concatenate Common's .pcss files with Admin's and Frontend's. It's not currently running autoprefixer or other PostCSS plugins. This is an issue with Parcel 1 that I wasn't able to resolve and shipped anyway because it's still an overall improvement to the boilerplate. Parcel 2 should eventually resolve this issue.
- Rename PHP class file names to match class names, including capitalization, according to PSR-4
- Editable JS and CSS (moved to SCSS) moved to
development
folder and npm build process implemented
- Declutter main plugin file by creating new
Bootstrap
class - Now requires PHP version 7.1.0 (up from 5.6.0)
- Added argument type and return type declarations (including scalar, which is why 7.1+ is needed, plus 7.0 was deprecated as of December 3, 2018)
- Refactor classes to be smaller and more intentional, including multiple utilities classes and consolidating settings
- Moved defines and related to
Plugin_Data
class (has static methods because of hard-coded values) - Created abstract
Shortcode()
class, which should be extended when creating your own new shortcodes ([tk_request]
is still included as an example) - Fix
class_exists()
checks to be namespace-aware - Remove unused
libraries
andviews
directories throughout
- Fix
tk_request()
in Common to better support array values
- Added
output_to_log()
utility function to Common to enable writing toWP_DEBUG_LOG
and optionally send an email, such as to the site administrator. - Renamed
wp_plugin_name_get_plugin_display_name()
toget_plugin_display_name()
to remove prefix since we are within our own namespace.
- Add
string_ends_with()
andget_string_between_two_strings()
utility functions.
- Simplify the CSS and JS file names to speed up initial setup by avoiding unnecessary file renaming.
- Simplify boilerplate's repository files so boilerplate can be ran as a plugin itself ("out of the box" as they say), which helps with testing things work before committing changes to the repo.
- Fix logic for Common's
get_option()
andget_option_as_array()
.
- Add link to plugin options screen in the Plugins List admin screen.
- Add plugin options screen that links to WordPress Customizer panel.
- Add methods for getting all Customizer options, deleting all options, and getting a single option (as raw, string, or array).
- Add a custom Customizer Control for multiple checkboxes, optionally sortable. Big thanks to Scott Fennell for the start to the code and permission to use. The version included here is heavily modified and follows this repository's license. Still needs work if wanting to use
<select>
within each checkbox. - Add example Customizer options to help get a quick start.
- Add utility function to detect current URL.
- Add utility function to get public post types, sorted by their labels.
- Tweak -
Common
as class constructor (dependency injection) instead of singleton instance. These articles provide simple examples and explanations if you are curious. - Tweak - Add try/catch around DateTime(), although it shouldn't actually affect code.
- Tweak - Wrap each class within
class_exists()
. - Tweak - Add
ABSPATH
check to top of all PHP files. - Tweak - Remove all
@since
and@access
tags. Remove all@link
tags to the example link.
- Add
flatten_array()
utility method to Common.
- Fix loading logic regarding Admin and Frontend to allow both to run during Ajax.
- Fix to allow Admin hooks to run during Ajax.
- Add ability to require a parent and/or child theme.
- Implement TGM Plugin Activation for required plugins (does not handle requiring a theme). At this time, it does not handle non-bundled premium plugins very well (adding incorrect download links to the TGMPA admin screen), but it does enhance some functionality:
- displaying plugin nice name
- requiring a minimum version number
- adding the ability to mark a plugin recommended without being required
- adding the ability to link to the plugin (the only way to tell people where to download the plugin manually)
- Improve main plugin class' loading, removing static methods and singleton.
Common
class: Use a singleton instead of static methods.- Removed all
@author
DocBlocks, per WordPress' best practices:-
It is WordPress policy not to use the
@author
tag, except in the case of maintaining it in external libraries. We do not want to imply any sort of "ownership" over code that might discourage contribution.
-
- Fix
Common::post_id_helper()
to not return0
when passed0
. Instead, will go through to the logic to automatically determine the Post ID.
- Now requires Composer. See instructions, above.
- Fix
Common::tk_request()
and add new$default
and$escape
parameters.
- Added 'ABSPATH' checks at the beginning of all PHP files
- Added
wp_plugin_name_get_plugin_display_name()
to main plugin file - Removed
PLUGIN_NAME
constant and replaced all usage withPLUGIN_TEXT_DOMAIN
since they were duplicates (as they should have been). Kept it as "plugin_text_domain" in the name instead of something like "plugin_id" or "plugin_slug" to help IDE autocomplete suggestions when using translation functions. - Added
plugin_text_domain_underscores()
to Common
- Added a few nice helper methods to Common
- Improved readme.txt
- Initial Release
Thanks goes to these wonderful people (emoji key):
Stefan Jöbstl 💻 |
Neal Fennimore 👀 |
Scott Fennell 💻 |
Hardeep Asrani 💡 |
Florian Rappl 🔌 |
theAverageDev (Luca Tumedei) |
This project follows the all-contributors specification. Contributions of any kind welcome!