d36bb954d1
Baseline before backporting wp-data-optimizer v3.0.1-v3.4.6. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TbG1keQQ7XBa7qMQY16KCY
184 lines
6.5 KiB
PHP
184 lines
6.5 KiB
PHP
<?php
|
|
/**
|
|
* TMDO_API — public facade for partner plugins.
|
|
*
|
|
* Provides the canonical, stable API contract for reading/writing entity
|
|
* fields managed by WP Data Optimizer. Partner plugins MUST use this API
|
|
* instead of direct `get_post_meta()`, `get_user_meta()`, etc., when the
|
|
* field is registered to WPDO.
|
|
*
|
|
* Part A.4 of the Anti-EAV Playbook (plan file).
|
|
*
|
|
* Usage:
|
|
* TMDO_API::get_field( $post_id, 'hp_price' ); // post entity
|
|
* TMDO_API::get_entity( 'user', $uid, 'points' ); // any entity
|
|
* TMDO_API::set_field( $post_id, 'hp_price', '100.00' );
|
|
* TMDO_API::query( [ 'post_type' => 'hp_listing', 'meta_query' => [...] ] );
|
|
*
|
|
* @package WP_Data_Optimizer
|
|
* @since 2.0.0
|
|
*/
|
|
|
|
if ( ! defined( 'ABSPATH' ) ) {
|
|
exit;
|
|
}
|
|
|
|
/**
|
|
* Public read/write facade. All methods are static and side-effect free
|
|
* outside of the underlying meta API.
|
|
*
|
|
* Backwards compatibility: this class is the long-lived contract for
|
|
* partner plugins. Internal implementation may change between versions
|
|
* (e.g. switch from postmeta passthrough to Hook Bus dispatch); the
|
|
* signatures here are guaranteed stable across the 2.x line.
|
|
*/
|
|
final class TMDO_API {
|
|
|
|
/**
|
|
* Get a single meta field for a post.
|
|
*
|
|
* Internally:
|
|
* - When the field is registered to a Zone, reads from the zone table
|
|
* once cutover.
|
|
* - When the field is unregistered, falls back to native get_post_meta()
|
|
* transparently.
|
|
*
|
|
* @param int $post_id Post ID.
|
|
* @param string $meta_key Meta key.
|
|
* @param bool $single Whether to return a single scalar.
|
|
* @return mixed Meta value (or empty string when single=true and absent).
|
|
*/
|
|
public static function get_field( int $post_id, string $meta_key, bool $single = true ) {
|
|
// Defer to standard WordPress metadata API. The Hook Bus / Sync_Bridge
|
|
// transparently routes the read to the appropriate zone table when
|
|
// the module is in a read-custom state (cutover/cleanup/complete).
|
|
// This single line of indirection is the contract that lets us swap
|
|
// internal storage without breaking callers.
|
|
return get_post_meta( $post_id, $meta_key, $single );
|
|
}
|
|
|
|
/**
|
|
* Get a single meta field for any entity (post/user/term/comment).
|
|
*
|
|
* @param string $entity_type One of: post, user, term, comment.
|
|
* @param int $entity_id Entity ID.
|
|
* @param string $meta_key Meta key.
|
|
* @param bool $single Whether to return a single scalar.
|
|
* @return mixed
|
|
*/
|
|
public static function get_entity( string $entity_type, int $entity_id, string $meta_key, bool $single = true ) {
|
|
return match ( $entity_type ) {
|
|
'post' => get_post_meta( $entity_id, $meta_key, $single ),
|
|
'user' => get_user_meta( $entity_id, $meta_key, $single ),
|
|
'term' => get_term_meta( $entity_id, $meta_key, $single ),
|
|
'comment' => get_comment_meta( $entity_id, $meta_key, $single ),
|
|
default => null,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Set a single meta field for a post.
|
|
*
|
|
* @param int $post_id Post ID.
|
|
* @param string $meta_key Meta key.
|
|
* @param mixed $meta_value New value.
|
|
* @return bool|int Result of update_post_meta.
|
|
*/
|
|
public static function set_field( int $post_id, string $meta_key, $meta_value ) {
|
|
return update_post_meta( $post_id, $meta_key, $meta_value );
|
|
}
|
|
|
|
/**
|
|
* Set a meta field for any entity.
|
|
*
|
|
* @param string $entity_type One of: post, user, term, comment.
|
|
* @param int $entity_id Entity ID.
|
|
* @param string $meta_key Meta key.
|
|
* @param mixed $meta_value New value.
|
|
* @return bool|int
|
|
*/
|
|
public static function set_entity( string $entity_type, int $entity_id, string $meta_key, $meta_value ) {
|
|
return match ( $entity_type ) {
|
|
'post' => update_post_meta( $entity_id, $meta_key, $meta_value ),
|
|
'user' => update_user_meta( $entity_id, $meta_key, $meta_value ),
|
|
'term' => update_term_meta( $entity_id, $meta_key, $meta_value ),
|
|
'comment' => update_comment_meta( $entity_id, $meta_key, $meta_value ),
|
|
default => false,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Run a meta_query — currently a thin wrapper around WP_Query.
|
|
*
|
|
* Future: when v2.0.0 entity adapters are fully wired, this method will
|
|
* compile cross-entity queries via TMDO_Query_Compiler.
|
|
*
|
|
* @param array $args WP_Query-compatible arguments.
|
|
* @return WP_Query
|
|
*/
|
|
public static function query( array $args ): WP_Query {
|
|
return new WP_Query( $args );
|
|
}
|
|
|
|
/**
|
|
* Check whether a field is registered to WPDO (zone or entity).
|
|
*
|
|
* Useful for partner plugins to choose between TMDO_API and native APIs.
|
|
*
|
|
* @param string $entity_type One of: post, user, term, comment.
|
|
* @param string $meta_key Meta key.
|
|
* @return bool True when the field is registered.
|
|
*/
|
|
public static function is_field_registered( string $entity_type, string $meta_key ): bool {
|
|
// Zone path (post entity only): TMDO_Schema_Registry.
|
|
if ( 'post' === $entity_type && class_exists( 'TMDO_Schema_Registry' ) ) {
|
|
$registry = TMDO_Schema_Registry::instance();
|
|
// We can't filter without a post_type — search across all hot/cold/warm.
|
|
foreach ( $registry->all() as $field ) {
|
|
if ( ( $field['meta_key'] ?? '' ) === $meta_key ) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Entity path: TMDO_Entity_Registry (PR-1 ported).
|
|
if ( class_exists( 'TMDO_Entity_Registry' ) ) {
|
|
return TMDO_Entity_Registry::is_managed( $entity_type, $meta_key );
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Trace the storage backend currently serving a field.
|
|
*
|
|
* Returns one of: 'postmeta' (native fallback), 'zone_hot', 'zone_warm',
|
|
* 'zone_cold', 'zone_archive', 'entity_table', 'unregistered'.
|
|
*
|
|
* Useful for `wp wpdo doctor` and Conflict Detector reports.
|
|
*
|
|
* @param string $entity_type Entity type.
|
|
* @param string $meta_key Meta key.
|
|
* @param string $post_type Post type, for zone routing (post entity only).
|
|
* @return string Storage label.
|
|
*/
|
|
public static function trace_storage( string $entity_type, string $meta_key, string $post_type = '' ): string {
|
|
if ( 'post' === $entity_type && class_exists( 'TMDO_Schema_Registry' ) && '' !== $post_type ) {
|
|
$field = TMDO_Schema_Registry::instance()->get_field( $post_type, $meta_key );
|
|
if ( $field ) {
|
|
$module = ( $field['zone'] ?? 'hot' ) . '_' . sanitize_key( $post_type );
|
|
if ( class_exists( 'TMDO_Feature_Flags' ) && TMDO_Feature_Flags::is_read_custom( $module ) ) {
|
|
return 'zone_' . $field['zone'];
|
|
}
|
|
return 'postmeta';
|
|
}
|
|
}
|
|
|
|
if ( class_exists( 'TMDO_Entity_Registry' ) && TMDO_Entity_Registry::is_managed( $entity_type, $meta_key ) ) {
|
|
return 'entity_table';
|
|
}
|
|
|
|
return 'unregistered';
|
|
}
|
|
}
|