Make WordPress Core


Ignore:
Timestamp:
07/19/2018 06:48:52 PM (6 years ago)
Author:
kadamwhite
Message:

REST API: Support meta registration for specific object subtypes.

Introduce an object_subtype argument to the args array for register_meta() which can be used to limit meta registration to a single subtype (e.g. a custom post type or taxonomy, vs all posts or taxonomies).

Introduce register_post_meta() and register_term_meta() wrapper methods for register_meta to provide a convenient interface for the common case of registering meta for a specific taxonomy or post type. These methods work the way plugin developers have often expected register_meta to function, and should be used in place of direct register_meta where possible.

Props flixos90, tharsheblows, spacedmonkey.

Merges [43378] to the 4.9 branch.
Fixes #38323.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • branches/4.9/src/wp-includes/meta.php

    r42056 r43510  
    4545    }
    4646
     47    $meta_subtype = get_object_subtype( $meta_type, $object_id );
     48
    4749    $column = sanitize_key($meta_type . '_id');
    4850
     
    5052    $meta_key = wp_unslash($meta_key);
    5153    $meta_value = wp_unslash($meta_value);
    52     $meta_value = sanitize_meta( $meta_key, $meta_value, $meta_type );
     54    $meta_value = sanitize_meta( $meta_key, $meta_value, $meta_type, $meta_subtype );
    5355
    5456    /**
     
    158160    }
    159161
     162    $meta_subtype = get_object_subtype( $meta_type, $object_id );
     163
    160164    $column = sanitize_key($meta_type . '_id');
    161165    $id_column = 'user' == $meta_type ? 'umeta_id' : 'meta_id';
     
    166170    $passed_value = $meta_value;
    167171    $meta_value = wp_unslash($meta_value);
    168     $meta_value = sanitize_meta( $meta_key, $meta_value, $meta_type );
     172    $meta_value = sanitize_meta( $meta_key, $meta_value, $meta_type, $meta_subtype );
    169173
    170174    /**
     
    641645        }
    642646
     647        $meta_subtype = get_object_subtype( $meta_type, $object_id );
     648
    643649        // Sanitize the meta
    644650        $_meta_value = $meta_value;
    645         $meta_value = sanitize_meta( $meta_key, $meta_value, $meta_type );
    646         $meta_value = maybe_serialize( $meta_value );
     651        $meta_value  = sanitize_meta( $meta_key, $meta_value, $meta_type, $meta_subtype );
     652        $meta_value  = maybe_serialize( $meta_value );
    647653
    648654        // Format the data query arguments.
     
    936942 *
    937943 * @since 3.1.3
     944 * @since 4.9.8 The `$object_subtype` parameter was added.
    938945 *
    939946 * @param string $meta_key       Meta key.
     
    943950 * @return mixed Sanitized $meta_value.
    944951 */
    945 function sanitize_meta( $meta_key, $meta_value, $object_type ) {
     952function sanitize_meta( $meta_key, $meta_value, $object_type, $object_subtype = '' ) {
     953    if ( ! empty( $object_subtype ) && has_filter( "sanitize_{$object_type}_meta_{$meta_key}_for_{$object_subtype}" ) ) {
     954
     955        /**
     956         * Filters the sanitization of a specific meta key of a specific meta type and subtype.
     957         *
     958         * The dynamic portions of the hook name, `$object_type`, `$meta_key`,
     959         * and `$object_subtype`, refer to the metadata object type (comment, post, term or user),
     960         * the meta key value, and the object subtype respectively.
     961         *
     962         * @since 4.9.8
     963         *
     964         * @param mixed  $meta_value     Meta value to sanitize.
     965         * @param string $meta_key       Meta key.
     966         * @param string $object_type    Object type.
     967         * @param string $object_subtype Object subtype.
     968         */
     969        return apply_filters( "sanitize_{$object_type}_meta_{$meta_key}_for_{$object_subtype}", $meta_value, $meta_key, $object_type, $object_subtype );
     970    }
     971
    946972    /**
    947973     * Filters the sanitization of a specific meta key of a specific meta type.
     
    963989 * Registers a meta key.
    964990 *
     991 * It is recommended to register meta keys for a specific combination of object type and object subtype. If passing
     992 * an object subtype is omitted, the meta key will be registered for the entire object type, however it can be partly
     993 * overridden in case a more specific meta key of the same name exists for the same object type and a subtype.
     994 *
     995 * If an object type does not support any subtypes, such as users or comments, you should commonly call this function
     996 * without passing a subtype.
     997 *
    965998 * @since 3.3.0
    966999 * @since 4.6.0 {@link https://core.trac.wordpress.org/ticket/35658 Modified
    9671000 *              to support an array of data to attach to registered meta keys}. Previous arguments for
    9681001 *              `$sanitize_callback` and `$auth_callback` have been folded into this array.
     1002 * @since 4.9.8 The `$object_subtype` argument was added to the arguments array.
    9691003 *
    9701004 * @param string $object_type    Type of object this meta is registered to.
     
    9731007 *     Data used to describe the meta key when registered.
    9741008 *
     1009 *     @type string $object_subtype    A subtype; e.g. if the object type is "post", the post type. If left empty,
     1010 *                                     the meta key will be registered on the entire object type. Default empty.
    9751011 *     @type string $type              The type of data associated with this meta key.
    9761012 *                                     Valid values are 'string', 'boolean', 'integer', and 'number'.
     
    9951031
    9961032    $defaults = array(
     1033        'object_subtype'    => '',
    9971034        'type'              => 'string',
    9981035        'description'       => '',
     
    10351072    $args = wp_parse_args( $args, $defaults );
    10361073
     1074    $object_subtype = ! empty( $args['object_subtype'] ) ? $args['object_subtype'] : '';
     1075
    10371076    // If `auth_callback` is not provided, fall back to `is_protected_meta()`.
    10381077    if ( empty( $args['auth_callback'] ) ) {
     
    10461085    // Back-compat: old sanitize and auth callbacks are applied to all of an object type.
    10471086    if ( is_callable( $args['sanitize_callback'] ) ) {
    1048         add_filter( "sanitize_{$object_type}_meta_{$meta_key}", $args['sanitize_callback'], 10, 3 );
     1087        if ( ! empty( $object_subtype ) ) {
     1088            add_filter( "sanitize_{$object_type}_meta_{$meta_key}_for_{$object_subtype}", $args['sanitize_callback'], 10, 4 );
     1089        } else {
     1090            add_filter( "sanitize_{$object_type}_meta_{$meta_key}", $args['sanitize_callback'], 10, 3 );
     1091        }
    10491092    }
    10501093
    10511094    if ( is_callable( $args['auth_callback'] ) ) {
    1052         add_filter( "auth_{$object_type}_meta_{$meta_key}", $args['auth_callback'], 10, 6 );
     1095        if ( ! empty( $object_subtype ) ) {
     1096            add_filter( "auth_{$object_type}_meta_{$meta_key}_for_{$object_subtype}", $args['auth_callback'], 10, 6 );
     1097        } else {
     1098            add_filter( "auth_{$object_type}_meta_{$meta_key}", $args['auth_callback'], 10, 6 );
     1099        }
    10531100    }
    10541101
    10551102    // Global registry only contains meta keys registered with the array of arguments added in 4.6.0.
    10561103    if ( ! $has_old_auth_cb && ! $has_old_sanitize_cb ) {
    1057         $wp_meta_keys[ $object_type ][ $meta_key ] = $args;
     1104        unset( $args['object_subtype'] );
     1105
     1106        $wp_meta_keys[ $object_type ][ $object_subtype ][ $meta_key ] = $args;
    10581107
    10591108        return true;
     
    10671116 *
    10681117 * @since 4.6.0
     1118 * @since 4.9.8 The `$object_subtype` parameter was added.
    10691119 *
    10701120 * @param string $object_type    The type of object.
    10711121 * @param string $meta_key       The meta key.
    1072  *
    1073  * @return bool True if the meta key is registered to the object type. False if not.
    1074  */
    1075 function registered_meta_key_exists( $object_type, $meta_key ) {
     1122 * @param string $object_subtype Optional. The subtype of the object type.
     1123 *
     1124 * @return bool True if the meta key is registered to the object type and, if provided,
     1125 *              the object subtype. False if not.
     1126 */
     1127function registered_meta_key_exists( $object_type, $meta_key, $object_subtype = '' ) {
     1128    $meta_keys = get_registered_meta_keys( $object_type, $object_subtype );
     1129
     1130    return isset( $meta_keys[ $meta_key ] );
     1131}
     1132
     1133/**
     1134 * Unregisters a meta key from the list of registered keys.
     1135 *
     1136 * @since 4.6.0
     1137 * @since 4.9.8 The `$object_subtype` parameter was added.
     1138 *
     1139 * @param string $object_type    The type of object.
     1140 * @param string $meta_key       The meta key.
     1141 * @param string $object_subtype Optional. The subtype of the object type.
     1142 * @return bool True if successful. False if the meta key was not registered.
     1143 */
     1144function unregister_meta_key( $object_type, $meta_key, $object_subtype = '' ) {
    10761145    global $wp_meta_keys;
    10771146
    1078     if ( ! is_array( $wp_meta_keys ) ) {
    1079         return false;
    1080     }
    1081 
    1082     if ( ! isset( $wp_meta_keys[ $object_type ] ) ) {
    1083         return false;
    1084     }
    1085 
    1086     if ( isset( $wp_meta_keys[ $object_type ][ $meta_key ] ) ) {
    1087         return true;
    1088     }
    1089 
    1090     return false;
    1091 }
    1092 
    1093 /**
    1094  * Unregisters a meta key from the list of registered keys.
    1095  *
    1096  * @since 4.6.0
    1097  *
    1098  * @param string $object_type The type of object.
    1099  * @param string $meta_key    The meta key.
    1100  * @return bool True if successful. False if the meta key was not registered.
    1101  */
    1102 function unregister_meta_key( $object_type, $meta_key ) {
    1103     global $wp_meta_keys;
    1104 
    1105     if ( ! registered_meta_key_exists( $object_type, $meta_key ) ) {
    1106         return false;
    1107     }
    1108 
    1109     $args = $wp_meta_keys[ $object_type ][ $meta_key ];
     1147    if ( ! registered_meta_key_exists( $object_type, $meta_key, $object_subtype ) ) {
     1148        return false;
     1149    }
     1150
     1151    $args = $wp_meta_keys[ $object_type ][ $object_subtype ][ $meta_key ];
    11101152
    11111153    if ( isset( $args['sanitize_callback'] ) && is_callable( $args['sanitize_callback'] ) ) {
    1112         remove_filter( "sanitize_{$object_type}_meta_{$meta_key}", $args['sanitize_callback'] );
     1154        if ( ! empty( $object_subtype ) ) {
     1155            remove_filter( "sanitize_{$object_type}_meta_{$meta_key}_for_{$object_subtype}", $args['sanitize_callback'] );
     1156        } else {
     1157            remove_filter( "sanitize_{$object_type}_meta_{$meta_key}", $args['sanitize_callback'] );
     1158        }
    11131159    }
    11141160
    11151161    if ( isset( $args['auth_callback'] ) && is_callable( $args['auth_callback'] ) ) {
    1116         remove_filter( "auth_{$object_type}_meta_{$meta_key}", $args['auth_callback'] );
    1117     }
    1118 
    1119     unset( $wp_meta_keys[ $object_type ][ $meta_key ] );
     1162        if ( ! empty( $object_subtype ) ) {
     1163            remove_filter( "auth_{$object_type}_meta_{$meta_key}_for_{$object_subtype}", $args['auth_callback'] );
     1164        } else {
     1165            remove_filter( "auth_{$object_type}_meta_{$meta_key}", $args['auth_callback'] );
     1166        }
     1167    }
     1168
     1169    unset( $wp_meta_keys[ $object_type ][ $object_subtype ][ $meta_key ] );
    11201170
    11211171    // Do some clean up
     1172    if ( empty( $wp_meta_keys[ $object_type ][ $object_subtype ] ) ) {
     1173        unset( $wp_meta_keys[ $object_type ][ $object_subtype ] );
     1174    }
    11221175    if ( empty( $wp_meta_keys[ $object_type ] ) ) {
    11231176        unset( $wp_meta_keys[ $object_type ] );
     
    11311184 *
    11321185 * @since 4.6.0
    1133  *
    1134  * @param string $object_type The type of object. Post, comment, user, term.
     1186 * @since 4.9.8 The `$object_subtype` parameter was added.
     1187 *
     1188 * @param string $object_type    The type of object. Post, comment, user, term.
     1189 * @param string $object_subtype Optional. The subtype of the object type.
    11351190 * @return array List of registered meta keys.
    11361191 */
    1137 function get_registered_meta_keys( $object_type ) {
     1192function get_registered_meta_keys( $object_type, $object_subtype = '' ) {
    11381193    global $wp_meta_keys;
    11391194
    1140     if ( ! is_array( $wp_meta_keys ) || ! isset( $wp_meta_keys[ $object_type ] ) ) {
     1195    if ( ! is_array( $wp_meta_keys ) || ! isset( $wp_meta_keys[ $object_type ] ) || ! isset( $wp_meta_keys[ $object_type ][ $object_subtype ] ) ) {
    11411196        return array();
    11421197    }
    11431198
    1144     return $wp_meta_keys[ $object_type ];
     1199    return $wp_meta_keys[ $object_type ][ $object_subtype ];
    11451200}
    11461201
    11471202/**
    11481203 * Retrieves registered metadata for a specified object.
     1204 *
     1205 * The results include both meta that is registered specifically for the
     1206 * object's subtype and meta that is registered for the entire object type.
    11491207 *
    11501208 * @since 4.6.0
     
    11551213 *                            metadata for the specified object.
    11561214 * @return mixed A single value or array of values for a key if specified. An array of all registered keys
    1157  *               and values for an object ID if not.
     1215 *               and values for an object ID if not. False if a given $meta_key is not registered.
    11581216 */
    11591217function get_registered_metadata( $object_type, $object_id, $meta_key = '' ) {
     1218    $object_subtype = get_object_subtype( $object_type, $object_id );
     1219
    11601220    if ( ! empty( $meta_key ) ) {
    1161         if ( ! registered_meta_key_exists( $object_type, $meta_key ) ) {
     1221        if ( ! empty( $object_subtype ) && ! registered_meta_key_exists( $object_type, $meta_key, $object_subtype ) ) {
     1222            $object_subtype = '';
     1223        }
     1224
     1225        if ( ! registered_meta_key_exists( $object_type, $meta_key, $object_subtype ) ) {
    11621226            return false;
    11631227        }
    1164         $meta_keys = get_registered_meta_keys( $object_type );
     1228
     1229        $meta_keys     = get_registered_meta_keys( $object_type, $object_subtype );
    11651230        $meta_key_data = $meta_keys[ $meta_key ];
    11661231
     
    11711236
    11721237    $data = get_metadata( $object_type, $object_id );
     1238    if ( ! $data ) {
     1239        return array();
     1240    }
    11731241
    11741242    $meta_keys = get_registered_meta_keys( $object_type );
    1175     $registered_data = array();
    1176 
    1177     // Someday, array_filter()
    1178     foreach ( $meta_keys as $k => $v ) {
    1179         if ( isset( $data[ $k ] ) ) {
    1180             $registered_data[ $k ] = $data[ $k ];
    1181         }
    1182     }
    1183 
    1184     return $registered_data;
     1243    if ( ! empty( $object_subtype ) ) {
     1244        $meta_keys = array_merge( $meta_keys, get_registered_meta_keys( $object_type, $object_subtype ) );
     1245    }
     1246
     1247    return array_intersect_key( $data, $meta_keys );
    11851248}
    11861249
     
    11911254 *
    11921255 * @access private
    1193  * @since  4.6.0
    1194  *
    1195  * @param  array $args         Arguments from `register_meta()`.
    1196  * @param  array $default_args Default arguments for `register_meta()`.
     1256 * @since 4.6.0
     1257 *
     1258 * @param array $args         Arguments from `register_meta()`.
     1259 * @param array $default_args Default arguments for `register_meta()`.
    11971260 *
    11981261 * @return array Filtered arguments.
    11991262 */
    12001263function _wp_register_meta_args_whitelist( $args, $default_args ) {
    1201     $whitelist = array_keys( $default_args );
    1202 
    1203     // In an anonymous function world, this would be better as an array_filter()
    1204     foreach ( $args as $key => $value ) {
    1205         if ( ! in_array( $key, $whitelist ) ) {
    1206             unset( $args[ $key ] );
    1207         }
    1208     }
    1209 
    1210     return $args;
    1211 }
     1264    return array_intersect_key( $args, $default_args );
     1265}
     1266
     1267/**
     1268 * Returns the object subtype for a given object ID of a specific type.
     1269 *
     1270 * @since 4.9.8
     1271 *
     1272 * @param string $object_type Type of object to request metadata for. (e.g. comment, post, term, user)
     1273 * @param int    $object_id   ID of the object to retrieve its subtype.
     1274 * @return string The object subtype or an empty string if unspecified subtype.
     1275 */
     1276function get_object_subtype( $object_type, $object_id ) {
     1277    $object_id      = (int) $object_id;
     1278    $object_subtype = '';
     1279
     1280    switch ( $object_type ) {
     1281        case 'post':
     1282            $post_type = get_post_type( $object_id );
     1283
     1284            if ( ! empty( $post_type ) ) {
     1285                $object_subtype = $post_type;
     1286            }
     1287            break;
     1288
     1289        case 'term':
     1290            $term = get_term( $object_id );
     1291            if ( ! $term instanceof WP_Term ) {
     1292                break;
     1293            }
     1294
     1295            $object_subtype = $term->taxonomy;
     1296            break;
     1297
     1298        case 'comment':
     1299            $comment = get_comment( $object_id );
     1300            if ( ! $comment ) {
     1301                break;
     1302            }
     1303
     1304            $object_subtype = 'comment';
     1305            break;
     1306
     1307        case 'user':
     1308            $user = get_user_by( 'id', $object_id );
     1309            if ( ! $user ) {
     1310                break;
     1311            }
     1312
     1313            $object_subtype = 'user';
     1314            break;
     1315    }
     1316
     1317    /**
     1318     * Filters the object subtype identifier for a non standard object type.
     1319     *
     1320     * The dynamic portion of the hook, `$object_type`, refers to the object
     1321     * type (post, comment, term, or user).
     1322     *
     1323     * @since 4.9.8
     1324     *
     1325     * @param string $object_subtype Empty string to override.
     1326     * @param int    $object_id      ID of the object to get the subtype for.
     1327     */
     1328    return apply_filters( "get_object_subtype_{$object_type}", $object_subtype, $object_id );
     1329}
Note: See TracChangeset for help on using the changeset viewer.