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

★ 0stars

Install

Download source zip No release zip yet

This is the repository archive. It installs, but the folder name will carry the branch suffix and updates will not flow.

Install straight from GitHub with WP-CLI:

wp plugin install https://github.com/zachary/wp-my-plugin/archive/refs/heads/master.zip
No built-in updater. New versions won't appear under Dashboard → Updates. Install new releases yourself, or let Git Updater track the repository.

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
  • Settings — records_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.