WP My Plugin
wordpress plugin development framework generated by claude code with modern PHP and WP SDK & API
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.zipReadme
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 allwpdbaccess. - Immutable model:
Recordis areadonlyvalue object. - Self-registering services: each service implements
Registrableand 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
wpdbtable. - REST API CRUD under
wp-my-plugin/v1/recordswith 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 runcomposer 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.phpis the source of truth — each entry declareshandle, file path, area (admin/frontend), and condition (screensubstring orshortcodetag).- Every manifest entry is
wp_register_*'d oninit, so handles exist for the entire request. - Admin entries are auto-enqueued on
admin_enqueue_scriptswhen 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 writewpmp_record_created/wpmp_record_updated/wpmp_record_deleted— react to changeswpmp_rest_records_query_args— adjust the REST collection querywpmp_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.