jooservices / wordpress-sdk
A strictly-typed, SOLID-compliant PHP SDK for WordPress REST API
Requires
- php: >=8.5
- jooservices/client: ^0.5.0
- jooservices/dto: ^1.0
- monolog/monolog: ^3.10
- php-di/php-di: ^7.1
- symfony/property-access: ^8.0
- symfony/property-info: ^8.0
- symfony/serializer: ^8.0
- symfony/validator: ^8.0
Requires (Dev)
- captainhook/captainhook: ^5.27
- fakerphp/faker: ^1.24
- infection/infection: ^0.32.3
- laravel/pint: ^1.27
- phpmd/phpmd: ^2.15
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^12.5
- squizlabs/php_codesniffer: ^4.0
- vlucas/phpdotenv: ^5.6
- dev-develop
- v1.2.0
- 1.1.0
- 1.0.1
- dev-fix/reconcile-status-dto-slug
- dev-fix/reconcile-dto-field-coverage
- dev-chore/sdk-scope-coverage-matrix
- dev-master
- dev-release/1.2.0
- dev-fix/prepare-release-1.2.0-local-sync
- dev-refactor/extract-content-templates
- dev-chore/coverage-gates-and-docs
- dev-fix/service-pagination-and-endpoints
- dev-feat/story-template-chapters
- dev-release/1.1.0
- dev-fix/release-readiness-coverage
This package is auto-updated.
Last update: 2026-05-17 12:11:25 UTC
README
DTO-first PHP 8.5 SDK for the WordPress REST API, built for typed content automation, pagination, media, settings, users, taxonomies, discovery, and custom/plugin endpoints.
Package name: jooservices/wordpress-sdk
Install
composer require jooservices/wordpress-sdk
Quick start
use JOOservices\WordPress\Sdk\Data\Query\ListPostsQuery; use JOOservices\WordPress\Sdk\WordPressService; $wordpress = WordPressService::create( baseUrl: getenv('WORDPRESS_URL'), username: getenv('WORDPRESS_USER'), password: getenv('WORDPRESS_APP_PASSWORD'), ); $posts = $wordpress->posts()->list(new ListPostsQuery( perPage: 10, search: 'automation', fields: 'id,title,link,date', embed: true, )); foreach ($posts as $post) { echo $post->title->rendered . PHP_EOL; }
Supported endpoints
| Endpoint | Service | Status |
|---|---|---|
| Posts | posts() |
CRUD |
| Pages | pages() |
CRUD |
| Media | media() |
list/get/upload/delete |
| Users | users() |
CRUD + me() |
| Comments | comments() |
CRUD |
| Categories | categories() |
CRUD |
| Tags | tags() |
CRUD |
| Search | search() |
list/search |
| Taxonomies | taxonomies() |
list/get |
| Post types | postTypes() |
list/get |
| Statuses | statuses() |
list/get |
| Application Passwords | applicationPasswords() |
list/get/create/delete/deleteAll |
| Settings | settings() |
get/update |
| Discovery / schema | discovery() |
index/routes/schema |
| Custom endpoints | custom() |
GET/POST/PUT/PATCH/DELETE raw arrays |
| Revisions | revisions() |
posts/pages/block autosaves |
| Plugins | plugins() |
raw admin operations |
| Themes | themes() |
raw admin reads |
| Blocks / block types / renderer / directory | blocks(), blockTypes(), blockRenderer(), blockDirectory() |
raw editor operations |
| Menus / navigation | menuLocations(), navigations(), navMenus(), navMenuItems() |
raw navigation operations |
| Templates / template parts / global styles | templates(), templateParts(), globalStyles() |
raw block theme operations |
| Widgets / sidebars | widgets(), widgetTypes(), sidebars() |
raw widget operations |
| Site Health | siteHealth() |
raw diagnostic test reads |
See docs/05-maintenance/02-wordpress-rest-api-coverage-matrix.md for the post-1.2.0 endpoint-by-endpoint coverage matrix, DTO/query coverage, integration status, capability requirements, and documented gaps against the official WordPress REST API reference.
SDK core boundary
SDK core covers the native WordPress REST API client surface, typed DTOs, query DTOs, auth, pagination, discovery/schema helpers, error mapping, and generic REST payload helpers.
Not SDK core:
- opinionated content templates for stories, reviews, articles, or other editorial formats
- publishing workflows or editorial orchestration that go beyond the native REST API surface
- plugin/theme implementation logic that is specific to one plugin, theme, or content product
SDK helper extras
The SDK also ships with optional developer-experience helpers under JOOservices\WordPress\Sdk\Support.
PostBuilderhelps assemble post payloads fluently before callingposts()->create()orupdate().ContentBuilderhelps generate Gutenberg-compatible block markup in PHP.
These helpers are not native WordPress REST API resources. They remain in the SDK because they are generic payload and Gutenberg markup helpers for normal WordPress post operations.
Content templates for stories, product reviews, articles, and template registries now live in jooservices/wordpress-content-templates. That package depends on this SDK and generates SDK-compatible post/page payloads; this SDK does not depend on it.
If a helper becomes tightly coupled to a specific plugin or theme contract, treat it as a maintenance candidate for extraction or deprecation rather than expanding SDK core around that dependency.
DTO-first querying
The SDK accepts either raw query arrays or typed query DTOs for list and read operations.
use JOOservices\WordPress\Sdk\Data\Query\ListCommentsQuery; $comments = $wordpress->comments()->list(new ListCommentsQuery( post: 42, status: 'approve', perPage: 25, fields: 'id,content,date', ));
Pagination
WordPress pagination headers are exposed through PaginatedCollection. List-style services provide auto-pagination helpers where WordPress returns paginated collections.
$media = $wordpress->media()->list(['per_page' => 20, 'page' => 2]); printf( "Loaded %d items out of %d across %d pages\n", count($media), $media->total, $media->totalPages, );
foreach ($wordpress->posts()->cursor(['per_page' => 50]) as $post) { // Streams one page at a time. } $wordpress->posts()->each(function ($post): bool { return $post->id !== 123; // return false to stop early }, ['status' => 'publish']);
Use all() only when loading every matching post into memory is acceptable.
Site settings and custom routes
$settings = $wordpress->settings()->get(); $wordpress->settings()->update(['title' => 'New title']); $routes = $wordpress->discovery()->routes(); $schema = $wordpress->discovery()->schema('/wp/v2/posts'); $items = $wordpress->custom()->get('/my-plugin/v1/items', ['page' => 1]); $created = $wordpress->custom()->post('/my-plugin/v1/items', ['name' => 'Example']);
Custom endpoint paths are relative to the configured WordPress REST API root. Full external URLs are rejected.
Application passwords
$password = $wordpress->applicationPasswords()->create('me', [ 'name' => 'Publishing Worker', ]);
WordPress returns the raw generated application password only in the create response. Store it immediately in a secret manager and never log it.
Admin and editor endpoint groups
Broad WordPress admin/editor endpoint groups are exposed as raw arrays until their response schemas prove stable enough for public DTO contracts.
$plugins = $wordpress->plugins()->list(); $themes = $wordpress->themes()->list(); $revisions = $wordpress->revisions()->posts(123)->list(); $rendered = $wordpress->blockRenderer()->render('core/latest-posts', ['postsToShow' => 3]); $templates = $wordpress->templates()->list(); $health = $wordpress->siteHealth()->backgroundUpdates();
Most of these endpoints require authenticated users with admin/editor capabilities. Block renderer requests are sent with WordPress editor context because the REST renderer endpoint validates dynamic blocks against editor-only route context.
Error handling
use JOOservices\WordPress\Sdk\Exceptions\UnauthorizedException; use JOOservices\WordPress\Sdk\Exceptions\WordPressApiException; try { $wordpress->posts()->get(123, ['context' => 'edit']); } catch (UnauthorizedException $exception) { // Refresh credentials or verify the app password. } catch (WordPressApiException $exception) { $payload = $exception->toArray(); // sanitized diagnostic payload }
Documentation
Start with:
- Documentation hub
- Architecture overview
- Installation
- Quick start
- User guide
- Development
- Risks and scope boundary
- WordPress REST API coverage matrix
Development
composer lint
composer lint:all
composer test
composer test:integration
composer test:coverage
composer test:coverage:gate
composer test:coverage-map
composer quality
composer check
composer ci
Run composer test:integration only against a disposable local WordPress test site created for SDK testing. The stale Docker/WP Composer aliases from pre-release documentation are not shipped in 1.2.0 because their runner files are not present in this repository.
composer test:coverage now includes a Clover XML audit gate. Aggregate statement coverage must stay at or above 90%, and no coverable production file, class, or method in src/ may remain at 0% coverage unless it has a documented exclusion reason in tools/test-coverage-gate.php.
Security
Use WordPress application passwords through environment variables. Do not commit live credentials, and do not log authorization headers or raw secrets. See SECURITY.md.