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, no version field.

From code to site

Our release script ships a plugin in one go:

  1. Plugin repository: bump the version in the header and the version constant, commit, tag v<version>, push.
  2. Site repository: composer update for that plugin, commit composer.lock, push to staging: staging deploys automatically.
  3. Production: merge staging into main and run the production deploy manually.

The version is always the Git tag. See Composer and GitHub Actions.

Decisions every plugin needs

DecisionOur practice
Minimum PHPPublic plugins mostly support PHP 7.4; newer ones, like SEO Helper and LMS4WP, require 8.0
Shared codeCommon parts go into 4WP Bundle, not copied between plugins
DataCustom tables when data is queried and counted, not huge postmeta blobs
SecurityNonces, capabilities, escaping, guard clauses. See Security
StandardsWordPress 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.

Related