A PHP library for building WordPress plugins and themes that are secure by default, translatable, and safe to run next to other plugins built on a different version of it.
Status: 1.0 is being finished on the
nextbranch.mainstill holds 0.x — pin a tag, notdev-main. Coming from 0.x? Migrating from 0.x — no data migration is needed.
- Boot that never takes a site down. Your main file goes through a guard that parses on PHP 5.6: on an old PHP or WordPress, or if your plugin throws while starting, the site keeps working and administrators see why.
- Two plugins, two versions, one site. Everything WPToolkit puts into WordPress — hooks, option
keys, REST namespaces, script handles,
window.wptoolkit[slug]— is prefixed with your slug, and a copy scoped to your namespace can't collide with anyone else's. An incompatible copy refuses to activate and names the plugin that won. - Deny-by-default HTTP. One pipeline serves REST, admin-ajax and public pages: every route must say who may call it, input is validated before your code runs, and rate limits and real HTTP status codes come built in. A JS client calls routes by name.
- Data. Fields with pluggable types, meta boxes, entities stored as post types, custom tables or options behind one repository contract, a query builder with capped pages, search that ORs title, meta and terms, admin list columns with quick edit, and versioned migrations (batched, locked, reversible).
- Admin and front end. Settings on the Settings API — sensitive values readable only by name and optionally encrypted — admin pages, per-user notices, PHP views with theme overrides and an escaper, assets enqueued only at the right moment (RTL stylesheets included), and pretty front-end URLs.
- Tooling.
wp {your-slug}commands (toolkit:info,routes:list,migrate,make:entity, …) andwptoolkit-buildfor reproducible, verified release zips. - No runtime dependencies. PSR-3/11/16 bridges are there if you want them.
PHP 8.1+, WordPress 6.4+. Composer is optional.
composer config repositories.wptoolkit vcs https://github.com/codad5/wptoolkitcomposer require codad5/wptoolkit:^1.0Without Composer, bundle the standalone zip from the GitHub release and scope it to your namespace — see scoping and coexistence.
my-books.php:
<?php
/**
* Plugin Name: My Books
* Requires PHP: 8.1
* Text Domain: my-books
*/
if (!defined('ABSPATH')) {
exit;
}
call_user_func(require __DIR__ . '/vendor/codad5/wptoolkit/bootstrap/guard.php', __FILE__, array(
'php' => '8.1',
'wp' => '6.4',
'toolkit' => '^1.0',
), function () {
\Codad5\WPToolkit\Foundation\Application::create(__FILE__, ['slug' => 'my-books'])
->providers([\MyBooks\BooksServiceProvider::class])
->boot();
});src/Book.php and src/BooksServiceProvider.php:
#[PostType('book', public: true, columns: ['title' => 'post_title'])]
final class Book extends Entity
{
public static function fields(FieldFactory $f): array
{
return [
$f->text('title')->label(__('Title', 'my-books'))->required(),
$f->text('isbn')->label(__('ISBN', 'my-books'))->rules('max:17'),
];
}
}
final class BooksServiceProvider extends ServiceProvider
{
public function boot(EntityRegistrar $entities, Router $router): void
{
$entities->entity(Book::class, __('Book details', 'my-books')); // post type + edit screen
$router->get('books/{id}', [BookController::class, 'show'])
->public() // omit this and it won't register
->args(['id' => ['rules' => 'required|integer', 'type' => 'int']]);
}
}GET /wp-json/my-books/v1/books/42 now works, with id validated before BookController::show()
runs. The todo example is a complete plugin: settings, list columns, a front-end
page, an API and an Arabic translation.
| Guide | |
|---|---|
| Getting started | A scoped plugin with an entity, a route and a translated string |
| Routes and security | Access rules, validation, rate limits, the JS client |
| Data | Fields, meta boxes, entities, queries, search, migrations |
| Admin and front end | Settings, pages, notices, views, assets, public pages |
| Scoping and coexistence | Shipping without conflicts |
| Migrating from 0.x | Class by class, from real plugins |
Design decisions are recorded as ADRs; the architecture and phase plan explain how it fits together. Agents: start at llms.txt.
GPL-2.0-or-later.