kaiseki / wp-meta
Fluent, type-safe DSL for registering WordPress meta.
Requires
- php: ^8.2
- kaiseki/config: ^2.0
- kaiseki/wp-hook: ^2.0
- psr/container: ^1.1 || ^2.0
Requires (Dev)
- bnf/phpstan-psr-container: ^1.1
- brain/monkey: ^2.6
- kaiseki/php-coding-standard: ^1.0
- maglnet/composer-require-checker: ^4.0
- mockery/mockery: ^1.6
- php-stubs/wordpress-stubs: ^6.2
- phpstan/extension-installer: ^1.4
- phpstan/phpstan: ^2.0
- phpstan/phpstan-phpunit: ^2.0
- phpstan/phpstan-strict-rules: ^2.0
- phpunit/phpunit: ^11.0
- szepeviktor/phpstan-wordpress: ^2.0
This package is auto-updated.
Last update: 2026-06-03 12:19:27 UTC
README
Type-safe WordPress meta registration through a fluent field DSL.
You describe each meta field with a typed builder (StringField, IntegerField,
ObjectField, …). From that description the package builds the register_meta()
arguments and the REST show_in_rest JSON schema, so WordPress validates REST
requests against the field's type and constraints. The library stays intentionally
minimal — write-time sanitization is opt-in via withSanitizeCallback().
Installation
composer require kaiseki/wp-meta
Requires PHP 8.2 or newer.
Usage
Implement MetaDataBuilderInterface and return your MetaData definitions from
buildMetaData():
use Kaiseki\WordPress\Meta\Field\IntegerField; use Kaiseki\WordPress\Meta\Field\StringField; use Kaiseki\WordPress\Meta\MetaData; use Kaiseki\WordPress\Meta\MetaDataBuilderInterface; final class ProductMeta implements MetaDataBuilderInterface { /** * @return list<MetaData> */ public function buildMetaData(): array { return [ MetaData::post( 'product', 'sku', StringField::create()->withMaxLength(32), )->withShowInRest(), MetaData::post( 'product', 'stock', IntegerField::create(0)->withMinimum(0), )->withShowInRest(), ]; } }
Scalar fields
use Kaiseki\WordPress\Meta\Field\StringField; use Kaiseki\WordPress\Meta\Field\StringFormat; use Kaiseki\WordPress\Meta\MetaData; MetaData::post( 'page', 'contact_email', StringField::create() ->withFormat(StringFormat::Email) ->withMaxLength(254), )->withShowInRest();
Nullable fields
Nullability is opt-in and independent of the default. A field is non-nullable
unless you call ->nullable(), which makes null a valid value and emits a
['<type>', 'null'] schema type:
use Kaiseki\WordPress\Meta\Field\StringField; use Kaiseki\WordPress\Meta\MetaData; MetaData::post( 'page', 'subtitle', StringField::create()->nullable(), )->withShowInRest();
Numeric fields
use Kaiseki\WordPress\Meta\Field\NumberField; use Kaiseki\WordPress\Meta\MetaData; MetaData::post( 'product', 'rating', NumberField::create(0.0) ->withMinimum(0) ->withMaximum(5), )->withShowInRest();
Object fields
ObjectField composes other fields as properties. Mark properties as required
and decide whether extra keys are allowed with withAdditionalProperties():
use Kaiseki\WordPress\Meta\Field\IntegerField; use Kaiseki\WordPress\Meta\Field\ObjectField; use Kaiseki\WordPress\Meta\Field\StringField; use Kaiseki\WordPress\Meta\MetaData; MetaData::post( 'page', 'address', ObjectField::create() ->withProperty('street', StringField::create(), required: true) ->withProperty('zip', StringField::create()) ->withProperty('floor', IntegerField::create()) ->withAdditionalProperties(false), )->withShowInRest();
Array fields
use Kaiseki\WordPress\Meta\Field\ArrayField; use Kaiseki\WordPress\Meta\Field\StringField; use Kaiseki\WordPress\Meta\MetaData; MetaData::post( 'post', 'keywords', ArrayField::create(StringField::create()) ->withMaxItems(10) ->withUniqueItems(), )->withShowInRest();
Non-post object types
MetaData also targets terms, users, and comments. Terms take a taxonomy; users
and comments take only a meta key:
use Kaiseki\WordPress\Meta\Field\StringField; use Kaiseki\WordPress\Meta\MetaData; MetaData::term('category', 'color', StringField::create()) ->withShowInRest(); MetaData::user('bio', StringField::create()) ->withDescription('The user biography.') ->withShowInRest();
Registering the builder
The package ships a ConfigProvider that registers MetaDataRegistry as a hook
provider (via kaiseki/wp-hook) and wires its factory. List your builders under
the meta.data_builder config key — entries are container service ids the
registry resolves:
use Kaiseki\WordPress\Meta\ConfigProvider; return [ 'meta' => [ 'data_builder' => [ ProductMeta::class, ], ], ...(new ConfigProvider())(), ];
On the init hook the registry builds every definition and calls
register_meta() with the generated arguments.
Validation model
By design the library does as little as possible at runtime. From each field it
builds the register_meta() arguments and, when you call withShowInRest(), a
JSON schema. WordPress validates incoming REST requests against that schema
before they are written.
Writes that bypass REST (for example a direct update_post_meta() call) are
not sanitized by default — the value is stored as given. If you want
write-time coercion, opt in per definition with withSanitizeCallback(), which
maps onto register_meta()'s native sanitize_callback:
use Kaiseki\WordPress\Meta\Field\IntegerField; use Kaiseki\WordPress\Meta\MetaData; MetaData::post('product', 'stock', IntegerField::create(0)) ->withShowInRest() ->withSanitizeCallback(static fn(mixed $value): int => (int) $value);
Development
composer install
composer check # check-deps, cs-check, phpstan, phpunit
License
MIT — see LICENSE.