WP Manifestindependent plugin directory
manifest / developer / wp-my-plugin

WP My Plugin

wordpress plugin development framework generated by claude code with modern PHP and WP SDK & API

by zacharach · github.com/zachary/wp-my-plugin · website

0stars
0forks

Install

No release zip yet. The repository archive installs, but the folder name will carry the branch suffix and updates will not flow:

wp plugin install https://github.com/zachary/wp-my-plugin/archive/refs/heads/master.zip

Readme

WP My Plugin

A modern, low-coupling WordPress plugin scaffold that demonstrates the current mainstream PHP/WordPress stack — hooks, wpdb, and the WP REST API — through a small but complete Records manager feature.

  • PHP 8.1+, namespaced, PSR-4 autoloaded via Composer.
  • Repository pattern: a single class (RecordRepository) owns all wpdb access.
  • Immutable model: Record is a readonly value object.
  • Self-registering services: each service implements Registrable and wires its own hooks, so the bootstrapper stays tiny and features stay decoupled.
  • Admin parent menu + submenus built with the Settings API and WP_List_Table (no build step).
  • Settings stored via the Options API; records stored in a custom wpdb table.
  • REST API CRUD under wp-my-plugin/v1/records with permission and schema checks.
  • Tests with PHPUnit + Brain Monkey (no database required), plus PHPCS (WPCS) and PHPStan.

Requirements

  • PHP >= 8.1
  • WordPress >= 6.4
  • Composer (for autoloading and dev tooling)

Installation (development)

composer install

Then symlink or copy this directory into wp-content/plugins/ and activate it from Plugins in wp-admin (or wp plugin activate wp-my-plugin).

The plugin requires the Composer autoloader. If vendor/ is missing it bails gracefully instead of fataling, but the admin/REST features won't load until you run composer install.

Project layout

wp-my-plugin.php          Entry point: constants, autoload, activation hooks, bootstrap
uninstall.php             Guarded cleanup (drops table + options)
src/
  Plugin.php              Composition root: builds services and registers them
  Contracts/Registrable.php
  Activation/             Activator + Deactivator
  Database/               Schema (dbDelta + migration) + RecordRepository (the only wpdb user)
  Model/                  Record (readonly VO) + RecordStatus (enum)
  Settings/               SettingsService (Options API)
  Admin/                  AdminMenu, RecordsListPage, RecordsListTable, SettingsPage
  Rest/                   RecordsController (WP_REST_Controller)
  Frontend/               RecordsShortcode ([wpmp_records])
  Assets/                 AssetRegistrar (auto-loads CSS/JS from assets/manifest.php)
assets/                   manifest.php + admin.css, admin.js, frontend.css, frontend.js
tests/                    PHPUnit unit tests (Brain Monkey)

Admin

A top-level WP My Plugin menu with submenus:

  • All Records — list table with add / edit / delete (nonce + capability protected)
  • Add New — create/edit form
  • Settingsrecords_per_page, public REST read access, enable shortcode

REST API

Base: wp-json/wp-my-plugin/v1/records

Method Route Capability
GET /records public read if enabled, else manage_options
POST /records manage_options
GET /records/{id} (same as list)
PUT/PATCH /records/{id} manage_options
DELETE /records/{id} manage_options
# List (uses an application password for auth)
curl -s -u admin:APP_PASSWORD https://example.test/wp-json/wp-my-plugin/v1/records

# Create
curl -s -u admin:APP_PASSWORD -X POST https://example.test/wp-json/wp-my-plugin/v1/records \
  -H 'Content-Type: application/json' \
  -d '{"title":"Hello","content":"World","status":"published"}'

Shortcode

[wpmp_records status="published" limit="5"]

Frontend CSS/JS are enqueued automatically by the shortcode at render time (only on pages that actually use it) — no manual wp_enqueue calls needed.

Assets (auto-load)

src/Assets/AssetRegistrar.php is the single point of contact for the plugin's CSS/JS:

  • assets/manifest.php is the source of truth — each entry declares handle, file path, area (admin / frontend), and condition (screen substring or shortcode tag).
  • Every manifest entry is wp_register_*'d on init, so handles exist for the entire request.
  • Admin entries are auto-enqueued on admin_enqueue_scripts when the current screen slug matches.
  • Frontend entries stay registered until a service (e.g. the shortcode) calls AssetRegistrar::enqueue().

To add a new asset, drop the file under assets/ and add one entry to assets/manifest.php — no other code changes required. Other plugins can hook the wpmp_assets_manifest filter to amend the list dynamically.

Extensibility (hooks)

The plugin exposes filters/actions so other code can extend it without edits:

  • wpmp_record_insert_data / wpmp_record_update_data — adjust data before write
  • wpmp_record_created / wpmp_record_updated / wpmp_record_deleted — react to changes
  • wpmp_rest_records_query_args — adjust the REST collection query
  • wpmp_assets_manifest — add or remove entries from the asset registry

Development

composer run lint      # php -l on all files
composer run phpcs     # WordPress Coding Standards
composer run phpstan   # static analysis (WordPress stubs)
composer run test      # PHPUnit unit tests (Brain Monkey, no DB)
composer run check     # all of the above

Integration tests (optional)

Unit tests mock WordPress and need no database. Full integration tests for the wpdb-backed repository methods require the WordPress test suite and MySQL — e.g. wp scaffold plugin-tests wp-my-plugin and bin/install-wp-tests.sh. This is left as an optional follow-up.

License

GPL-2.0-or-later.

Read the full README on GitHub →