jooservices/wordpress-sdk

A strictly-typed, SOLID-compliant PHP SDK for WordPress REST API

Maintainers

Package info

github.com/jooservices/wordpress-sdk

pkg:composer/jooservices/wordpress-sdk

Statistics

Installs: 15

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 1


README

CI PHP Version License: MIT Packagist Version

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.

  • PostBuilder helps assemble post payloads fluently before calling posts()->create() or update().
  • ContentBuilder helps 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:

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.