emcp-tooling

This commit is contained in:
John Smith
2026-09-03 19:06:59 -04:00
parent 69267182df
commit 5c4606fc77
3 changed files with 119 additions and 1 deletions
+17 -1
View File
@@ -1053,7 +1053,7 @@ class EMCP_Tools_Content_Abilities {
'properties' => array(
'post_id' => array( 'type' => 'integer', 'description' => __( 'The post ID.', 'emcp-tools' ) ),
'taxonomy' => array( 'type' => 'string', 'description' => __( 'Taxonomy name (e.g. category, post_tag).', 'emcp-tools' ) ),
'terms' => array( 'type' => 'array', 'items' => array( 'type' => array( 'integer', 'string' ) ), 'description' => __( 'Term IDs or names.', 'emcp-tools' ) ),
'terms' => array( 'type' => 'array', 'items' => array( 'type' => 'string' ), 'description' => __( 'Term IDs (as strings) or names. Numeric strings are coerced to IDs in the executor.', 'emcp-tools' ) ),
'mode' => array( 'type' => 'string', 'enum' => array( 'replace', 'append', 'remove' ), 'description' => __( 'Default: replace.', 'emcp-tools' ) ),
'create_missing' => array( 'type' => 'boolean', 'description' => __( 'Create term names that do not exist. Default: true.', 'emcp-tools' ) ),
),
@@ -1090,7 +1090,23 @@ class EMCP_Tools_Content_Abilities {
$mode = in_array( $mode, array( 'replace', 'append', 'remove' ), true ) ? $mode : 'replace';
$terms = array_values( (array) ( $input['terms'] ?? array() ) );
// Normalise incoming items: accept IDs and names; if a numeric string is
// supplied, cast to int so the no-create-missing path treats it as an ID
// (matches the existing `is_numeric` branch below for create_missing=false).
// Without this, the create-missing path would try to create a term whose
// name is `"461"`, which wp_insert_term rejects as a bad name.
$create_missing = ! isset( $input['create_missing'] ) || (bool) $input['create_missing'];
if ( 'remove' !== $mode && $create_missing ) {
$coerced = array();
foreach ( $terms as $term ) {
if ( is_numeric( $term ) ) {
$coerced[] = (int) $term;
} else {
$coerced[] = (string) $term;
}
}
$terms = $coerced;
}
// When not auto-creating, resolve names to existing term IDs and drop
// any name that doesn't exist (numeric IDs pass through untouched).
+20
View File
@@ -189,6 +189,26 @@ class EMCP_Tools_Schema_Compat {
}
}
// Flatten `type: [a, b, …]` (union) to a single non-null type. The WordPress
// Abilities API input validator on WP 7.1 rejects array-typed `items.type`
// with a generic "input[X] is not of type array" error before the executor
// runs. Picking the first non-null entry is the documented "lossy" way to
// express the union without losing the schema altogether. Callers that need
// both types should coerce at the executor (see `set-post-terms`).
if ( isset( $schema['type'] ) && is_array( $schema['type'] ) ) {
$flattened = null;
foreach ( $schema['type'] as $candidate ) {
if ( is_string( $candidate ) && 'null' !== $candidate ) {
$flattened = $candidate;
break;
}
}
if ( null === $flattened ) {
$flattened = 'string';
}
$schema['type'] = $flattened;
}
if ( isset( $schema['properties'] ) && is_array( $schema['properties'] ) ) {
if ( empty( $schema['properties'] ) ) {
$schema['properties'] = new \stdClass();
@@ -0,0 +1,82 @@
<?php
/**
* Tests EMCP_Tools_Schema_Compat::sanitize().
*
* Targeted at the union-type flatten added to fix `set-post-terms` shorting
* (the WordPress Abilities API input validator rejects `items.type` arrays).
* The class file is pure-array transforms and needs no WordPress.
*
* @package EMCP_Tools
*/
require_once dirname( __DIR__ ) . '/includes/class-schema-compat.php';
class SanitizeSchemaUnionItemsTypeTest extends \PHPUnit\Framework\TestCase {
public function test_flatten_items_type_integer_string_to_string(): void {
$schema = array(
'type' => 'array',
'items' => array( 'type' => array( 'integer', 'string' ) ),
);
$out = EMCP_Tools_Schema_Compat::sanitize( $schema );
$this->assertSame( 'string', $out['items']['type'] );
}
public function test_flatten_top_level_union_type(): void {
$schema = array(
'type' => array( 'integer', 'string' ),
);
$out = EMCP_Tools_Schema_Compat::sanitize( $schema );
$this->assertSame( 'string', $out['type'] );
}
public function test_flatten_skips_null_picks_first_non_null(): void {
$schema = array(
'type' => array( 'null', 'integer', 'string' ),
);
$out = EMCP_Tools_Schema_Compat::sanitize( $schema );
$this->assertSame( 'integer', $out['type'] );
}
public function test_flatten_all_null_falls_back_to_string(): void {
$schema = array(
'type' => array( 'null' ),
);
$out = EMCP_Tools_Schema_Compat::sanitize( $schema );
$this->assertSame( 'string', $out['type'] );
}
public function test_passthrough_string_type_unchanged(): void {
$schema = array( 'type' => 'integer' );
$out = EMCP_Tools_Schema_Compat::sanitize( $schema );
$this->assertSame( 'integer', $out['type'] );
}
public function test_flatten_via_recursion_through_properties(): void {
$schema = array(
'type' => 'object',
'properties' => array(
'terms' => array(
'type' => 'array',
'items' => array( 'type' => array( 'integer', 'string' ) ),
),
),
);
$out = EMCP_Tools_Schema_Compat::sanitize( $schema );
$this->assertSame( 'string', $out['properties']['terms']['items']['type'] );
}
public function test_flatten_via_recursion_through_items(): void {
$schema = array(
'type' => 'array',
'items' => array(
'type' => 'object',
'properties' => array(
'id' => array( 'type' => array( 'integer', 'string' ) ),
),
),
);
$out = EMCP_Tools_Schema_Compat::sanitize( $schema );
$this->assertSame( 'string', $out['items']['properties']['id']['type'] );
}
}