chore: initial snapshot of 2meet-data-optimizer v0.1.0
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
This commit is contained in:
@@ -0,0 +1,183 @@
|
||||
<?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';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user