A plugin is the main way to extend WordPress without touching core. We build a family of them: some are public on WordPress.org, others run only on our sites. This page shows how a plugin is put together and shipped, using our own plugins as the example.
Our plugin family
- Public on WordPress.org: 11 plugins, among them FAQ, Smart Link, Drive, SEO Helper, Notifications and Weather.
- 4WP Bundle: the shared foundation the other plugins build on.
- Site plugins: LMS4WP, AI Assistant, MCP Connector and others that run on 4wp.dev.
See them all in Plugins.
Anatomy of a plugin
flowchart TB
MAIN["Main file<br/>header, constants, bootstrap"] --> NS["Namespaced classes<br/>ForWP\\Plugin"]
NS --> HOOKS["Hooks<br/>actions and filters"]
NS --> REST["REST routes<br/>with permission checks"]
NS --> ABIL["Abilities<br/>for AI agents"]
MAIN --> COMP["composer.json<br/>package passport"]
- Main file: the plugin header (name, version,
Requires PHP), constants and the bootstrap. - Namespaced classes: our plugins use the
ForWP\…namespace. - Hooks: the extension points the plugin uses and offers. See Hooks.
- REST routes: every route with its own
permission_callback. - Abilities: newer plugins, like SEO Helper, register abilities so AI agents can work with them.
composer.json: the package passport:name(4wpdev/<slug>),type: wordpress-plugin, noversionfield.
From code to site
Our release script ships a plugin in one go:
- Plugin repository: bump the version in the header and the version constant, commit, tag
v<version>, push. - Site repository:
composer updatefor that plugin, commitcomposer.lock, push tostaging: staging deploys automatically. - Production: merge
stagingintomainand run the production deploy manually.
The version is always the Git tag. See Composer and GitHub Actions.
Decisions every plugin needs
| Decision | Our practice |
|---|---|
| Minimum PHP | Public plugins mostly support PHP 7.4; newer ones, like SEO Helper and LMS4WP, require 8.0 |
| Shared code | Common parts go into 4WP Bundle, not copied between plugins |
| Data | Custom tables when data is queried and counted, not huge postmeta blobs |
| Security | Nonces, capabilities, escaping, guard clauses. See Security |
| Standards | WordPress Coding Standards |
FAQ
Plugin or theme functionality?
If it must survive a theme change, it belongs in a plugin.
Do I need Composer for a plugin?
For a public plugin, no. For a project that installs plugins by version, every plugin needs a composer.json.
When should a plugin register abilities?
When AI agents should be able to read or change its data, with permission checks.