'hp_listing', 'meta_query' => [...] ] ); * * @package WP_Data_Optimizer * @since 2.0.0 */ declare(strict_types=1); 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'; } }