{"id":2728,"date":"2025-02-04T09:00:29","date_gmt":"2025-02-04T10:00:29","guid":{"rendered":"http:\/\/www.fdswebdesign.com\/?p=2728"},"modified":"2025-02-04T23:43:40","modified_gmt":"2025-02-04T23:43:40","slug":"howdy-modern-wordpress-plugin-boilerplate","status":"publish","type":"post","link":"http:\/\/www.fdswebdesign.com\/index.php\/2025\/02\/04\/howdy-modern-wordpress-plugin-boilerplate\/","title":{"rendered":"Howdy: Modern WordPress Plugin Boilerplate"},"content":{"rendered":"
WordPress is a popular blogging platform<\/a> built with PHP, and its extensibility is one of its greatest strengths. While you can create a plugin by dropping a single PHP file into your PHP has changed significantly with new features and syntax. For example, it now includes better and proper OOP features and autoloading. However, WordPress still promotes the old procedural programming approach, and it\u2019s not straightforward to include autoloading in your plugin.<\/p>\n This is why I created Howdy<\/a><\/strong>, a WordPress plugin boilerplate that aims to make using modern PHP concepts in WordPress plugin development easier.<\/p>\n Howdy focuses on essential tools<\/strong> that improve productivity without overcomplicating the workflow. Rather than forcing every modern PHP practice into it, it prioritizes two foundational features:<\/p>\n Namespacing in PHP organizes classes, functions, and constants into logical groups, similar to how folders structure files, to prevent naming collisions. For example, two plugins defining a Traditionally, WordPress relies on prefixes for isolation. Suppose your plugin is named \u201cSimple Security by Acme.\u201d You\u2019d typically prefix functions and classes with your organization name While namespaces can be used in WordPress plugins, adoption remains rare. This is because you can\u2019t use namespaces to their full potential without autoloading.<\/p>\n Autoloading classes in WordPress has two key limitations.<\/p>\n First, you can\u2019t autoload third-party libraries, like openai-php\/client<\/a><\/strong>, without prefixing their namespaces. If two plugins load the same library, conflicting definitions will crash the site.<\/p>\n Additionally, without using Composer, all functions, constants, or static files must be manually loaded with These are the two main issues that Howdy<\/strong> aims to tackle.<\/p>\n Once we have these two features properly set up, adopting other advanced PHP patterns, such as dependency injection<\/a> or facades<\/a>, becomes much easier.<\/p>\n So let\u2019s install Howdy<\/strong> and see it in action.<\/p>\n We can install Howdy with the Composer This command will create a new directory, If you want to create the project in a different folder, you can add the directory name at the end of the command, like below:<\/p>\n It will then ask you to input the plugin slug. The plugin slug is required and should be unique. If you plan to publish your plugin on WordPress.org, this slug will be used in the plugin URL, e.g., The plugin slug will also be used to determine the default plugin name, the namespace prefix, and more. As we can see below, it\u2019s smart enough to transform the slug into the appropriate format. For this example, we\u2019ll keep the default plugin name while changing the namespace to Once the inputs are completed, the necessary updates are made to the project files. For example, the file in Howdy comes pre-configured with these tools to streamline development:<\/p>\n Howdy<\/strong> uses a slightly unconventional structure for a WordPress plugin. However, if you\u2019re familiar with frameworks like Laravel<\/a> or Symfony<\/a>, you\u2019ll adapt quickly.<\/p>\n There are three main directories:<\/p>\n Now that we have the plugin boilerplate set up, we can easily install additional packages with Composer. For example, if we want to build a plugin with OpenAI integration, we can include the Howdy will automatically add a prefix to the namespaces of all classes in this package after it\u2019s installed.<\/p>\n You can also install packages specifically for development. For example, to install symfony\/var-dumper<\/strong><\/a>, a popular PHP package for debugging, you can run:<\/p>\n This package provides a more user-friendly debugging experience compared to PHP\u2019s native Lastly, Howdy provides several commands to prepare your plugin for release:<\/p>\n In this article, we\u2019ve explored Howdy and its benefits for WordPress plugin development.<\/p>\n Howdy is designed to modernize plugin development without overwhelming you. It avoids bloating your workflow with every trendy tool (e.g., PHPUnit, PHPStan, or TypeScript are not included by default), but you can add them later as needed.<\/p>\n By solving dependency conflicts and enabling Composer, Howdy bridges WordPress development with the broader PHP ecosystem, unlocking countless possibilities for building maintainable and scalable plugins.<\/p>\n The post Howdy: Modern WordPress Plugin Boilerplate<\/a> appeared first on Hongkiat<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":" WordPress is a popular blogging platform built with PHP, and its extensibility is one of its greatest strengths. While you can create a plugin by dropping a single PHP file into your wp-content\/plugins directory, the broader development practices for WordPress plugins haven\u2019t evolved much over the years-even as PHP itself has improved significantly. PHP has…<\/p>\n","protected":false},"author":1,"featured_media":2730,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[11],"tags":[],"class_list":["post-2728","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-inspiration"],"_links":{"self":[{"href":"http:\/\/www.fdswebdesign.com\/index.php\/wp-json\/wp\/v2\/posts\/2728","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/www.fdswebdesign.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.fdswebdesign.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.fdswebdesign.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/www.fdswebdesign.com\/index.php\/wp-json\/wp\/v2\/comments?post=2728"}],"version-history":[{"count":3,"href":"http:\/\/www.fdswebdesign.com\/index.php\/wp-json\/wp\/v2\/posts\/2728\/revisions"}],"predecessor-version":[{"id":2736,"href":"http:\/\/www.fdswebdesign.com\/index.php\/wp-json\/wp\/v2\/posts\/2728\/revisions\/2736"}],"wp:featuredmedia":[{"embeddable":true,"href":"http:\/\/www.fdswebdesign.com\/index.php\/wp-json\/wp\/v2\/media\/2730"}],"wp:attachment":[{"href":"http:\/\/www.fdswebdesign.com\/index.php\/wp-json\/wp\/v2\/media?parent=2728"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.fdswebdesign.com\/index.php\/wp-json\/wp\/v2\/categories?post=2728"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.fdswebdesign.com\/index.php\/wp-json\/wp\/v2\/tags?post=2728"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}wp-content\/plugins<\/code> directory, the broader development practices for WordPress plugins haven\u2019t evolved much over the years-even as PHP itself has improved significantly.<\/p>\n
<\/figure>\n
Scope<\/h4>\n
\n
Namespacing<\/h5>\n
Security<\/code> class won\u2019t conflict if each uses a unique namespace.<\/p>\n
Acme_<\/code> or
acme_<\/code>:<\/p>\n
\r\n\/\/ Prefix in a function name.\r\nfunction acme_check_security() {\r\n \/\/ Add security check logic here\r\n}\r\n\r\n\/\/ Prefix in a class name.\r\nclass Acme_Security {\r\n public function check() {\r\n \/\/ Add class-specific logic here\r\n }\r\n}\r\n<\/pre>\n
Autoloading<\/h5>\n
require_once<\/code>, increasing boilerplate.<\/p>\n
Installation<\/h4>\n
create-project<\/code> command:<\/p>\n
\r\ncomposer create-project syntatis\/howdy -s dev\r\n<\/pre>\n
howdy<\/code>, pull all the project files, and install the dependencies from Packagist<\/a>.<\/p>\n
\r\ncomposer create-project syntatis\/howdy -s dev acme-plugin\r\n<\/pre>\n
https:\/\/wordpress.org\/plugins\/{slug}\/<\/code>. For this example, we\u2019ll use
acme-plugin<\/code>.<\/p>\n
<\/figure>\n
Acme<\/code> instead of
AcmePlugin<\/code>.<\/p>\n
<\/figure>\n
app\/Plugin.php<\/code> will include the namespace and the prefix for the dependencies\u2019 namespace.<\/p>\n
<\/figure>\n
What\u2019s Included?<\/h4>\n
\n
\r\nnpm run start\r\n<\/pre>\n<\/li>\n<\/ul>\n
Directory Structure<\/h4>\n
\n
Installing External Dependencies<\/h4>\n
openai-php\/client<\/code> package using the following command:<\/p>\n
\r\ncomposer require openai-php\/client\r\n<\/pre>\n
\r\ncomposer require symfony\/var-dumper --dev\r\n<\/pre>\n
var_dump<\/code> function.<\/p>\n
<\/figure>\n
Preparing for Production<\/h4>\n
npm run build<\/code>: Builds all asset files, including JavaScript and stylesheets, in the
src<\/code> directory. These files are optimized and minified for production.<\/p>\n
composer run build<\/code>: Re-compiles the project and removes packages installed for development.<\/p>\n
composer run plugin:zip<\/code>: Creates an installable ZIP file for the plugin. Unnecessary files like dotfiles,
src<\/code> directories, and
node_modules<\/code> are excluded from the final archive.<\/p>\n
Wrapping Up<\/h4>\n