Make WordPress Core


Ignore:
Timestamp:
06/21/2018 09:06:50 PM (7 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.
Fixes #38323.

File:
1 edited

Legend:

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

    r43043 r43378  
    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    /**
     
    166168    }
    167169
     170    $meta_subtype = get_object_subtype( $meta_type, $object_id );
     171
    168172    $column    = sanitize_key( $meta_type . '_id' );
    169173    $id_column = 'user' == $meta_type ? 'umeta_id' : 'meta_id';
     
    174178    $passed_value = $meta_value;
    175179    $meta_value   = wp_unslash( $meta_value );
    176     $meta_value   = sanitize_meta( $meta_key, $meta_value, $meta_type );
     180    $meta_value   = sanitize_meta( $meta_key, $meta_value, $meta_type, $meta_subtype );
    177181
    178182    /**
     
    667671        }
    668672
     673        $meta_subtype = get_object_subtype( $meta_type, $object_id );
     674
    669675        // Sanitize the meta
    670676        $_meta_value = $meta_value;
    671         $meta_value  = sanitize_meta( $meta_key, $meta_value, $meta_type );
     677        $meta_value  = sanitize_meta( $meta_key, $meta_value, $meta_type, $meta_subtype );
    672678        $meta_value  = maybe_serialize( $meta_value );
    673679
     
    969975 *
    970976 * @since 3.1.3
     977 * @since 5.0.0 The `$object_subtype` parameter was added.
    971978 *
    972979 * @param string $meta_key       Meta key.
     
    976983 * @return mixed Sanitized $meta_value.
    977984 */
    978 function sanitize_meta( $meta_key, $meta_value, $object_type ) {
     985function sanitize_meta( $meta_key, $meta_value, $object_type, $object_subtype = '' ) {
     986    if ( ! empty( $object_subtype ) && has_filter( "sanitize_{$object_type}_meta_{$meta_key}_for_{$object_subtype}" ) ) {
     987
     988        /**
     989         * Filters the sanitization of a specific meta key of a specific meta type and subtype.
     990         *
     991         * The dynamic portions of the hook name, `$object_type`, `$meta_key`,
     992         * and `$object_subtype`, refer to the metadata object type (comment, post, term or user),
     993         * the meta key value, and the object subtype respectively.
     994         *
     995         * @since 5.0.0
     996         *
     997         * @param mixed  $meta_value     Meta value to sanitize.
     998         * @param string $meta_key       Meta key.
     999         * @param string $object_type    Object type.
     1000         * @param string $object_subtype Object subtype.
     1001         */
     1002        return apply_filters( "sanitize_{$object_type}_meta_{$meta_key}_for_{$object_subtype}", $meta_value, $meta_key, $object_type, $object_subtype );
     1003    }
     1004
    9791005    /**
    9801006     * Filters the sanitization of a specific meta key of a specific meta type.
     
    9961022 * Registers a meta key.
    9971023 *
     1024 * It is recommended to register meta keys for a specific combination of object type and object subtype. If passing
     1025 * an object subtype is omitted, the meta key will be registered for the entire object type, however it can be partly
     1026 * overridden in case a more specific meta key of the same name exists for the same object type and a subtype.
     1027 *
     1028 * If an object type does not support any subtypes, such as users or comments, you should commonly call this function
     1029 * without passing a subtype.
     1030 *
    9981031 * @since 3.3.0
    9991032 * @since 4.6.0 {@link https://core.trac.wordpress.org/ticket/35658 Modified
    10001033 *              to support an array of data to attach to registered meta keys}. Previous arguments for
    10011034 *              `$sanitize_callback` and `$auth_callback` have been folded into this array.
     1035 * @since 5.0.0 The `$object_subtype` argument was added to the arguments array.
    10021036 *
    10031037 * @param string $object_type    Type of object this meta is registered to.
     
    10061040 *     Data used to describe the meta key when registered.
    10071041 *
     1042 *     @type string $object_subtype    A subtype; e.g. if the object type is "post", the post type. If left empty,
     1043 *                                     the meta key will be registered on the entire object type. Default empty.
    10081044 *     @type string $type              The type of data associated with this meta key.
    10091045 *                                     Valid values are 'string', 'boolean', 'integer', and 'number'.
     
    10281064
    10291065    $defaults = array(
     1066        'object_subtype'    => '',
    10301067        'type'              => 'string',
    10311068        'description'       => '',
     
    10681105    $args = wp_parse_args( $args, $defaults );
    10691106
     1107    $object_subtype = ! empty( $args['object_subtype'] ) ? $args['object_subtype'] : '';
     1108
    10701109    // If `auth_callback` is not provided, fall back to `is_protected_meta()`.
    10711110    if ( empty( $args['auth_callback'] ) ) {
     
    10791118    // Back-compat: old sanitize and auth callbacks are applied to all of an object type.
    10801119    if ( is_callable( $args['sanitize_callback'] ) ) {
    1081         add_filter( "sanitize_{$object_type}_meta_{$meta_key}", $args['sanitize_callback'], 10, 3 );
     1120        if ( ! empty( $object_subtype ) ) {
     1121            add_filter( "sanitize_{$object_type}_meta_{$meta_key}_for_{$object_subtype}", $args['sanitize_callback'], 10, 4 );
     1122        } else {
     1123            add_filter( "sanitize_{$object_type}_meta_{$meta_key}", $args['sanitize_callback'], 10, 3 );
     1124        }
    10821125    }
    10831126
    10841127    if ( is_callable( $args['auth_callback'] ) ) {
    1085         add_filter( "auth_{$object_type}_meta_{$meta_key}", $args['auth_callback'], 10, 6 );
     1128        if ( ! empty( $object_subtype ) ) {
     1129            add_filter( "auth_{$object_type}_meta_{$meta_key}_for_{$object_subtype}", $args['auth_callback'], 10, 6 );
     1130        } else {
     1131            add_filter( "auth_{$object_type}_meta_{$meta_key}", $args['auth_callback'], 10, 6 );
     1132        }
    10861133    }
    10871134
    10881135    // Global registry only contains meta keys registered with the array of arguments added in 4.6.0.
    10891136    if ( ! $has_old_auth_cb && ! $has_old_sanitize_cb ) {
    1090         $wp_meta_keys[ $object_type ][ $meta_key ] = $args;
     1137        unset( $args['object_subtype'] );
     1138
     1139        $wp_meta_keys[ $object_type ][ $object_subtype ][ $meta_key ] = $args;
    10911140
    10921141        return true;
     
    11001149 *
    11011150 * @since 4.6.0
     1151 * @since 5.0.0 The `$object_subtype` parameter was added.
    11021152 *
    11031153 * @param string $object_type    The type of object.
    11041154 * @param string $meta_key       The meta key.
    1105  *
    1106  * @return bool True if the meta key is registered to the object type. False if not.
    1107  */
    1108 function registered_meta_key_exists( $object_type, $meta_key ) {
     1155 * @param string $object_subtype Optional. The subtype of the object type.
     1156 *
     1157 * @return bool True if the meta key is registered to the object type and, if provided,
     1158 *              the object subtype. False if not.
     1159 */
     1160function registered_meta_key_exists( $object_type, $meta_key, $object_subtype = '' ) {
     1161    $meta_keys = get_registered_meta_keys( $object_type, $object_subtype );
     1162
     1163    return isset( $meta_keys[ $meta_key ] );
     1164}
     1165
     1166/**
     1167 * Unregisters a meta key from the list of registered keys.
     1168 *
     1169 * @since 4.6.0
     1170 * @since 5.0.0 The `$object_subtype` parameter was added.
     1171 *
     1172 * @param string $object_type    The type of object.
     1173 * @param string $meta_key       The meta key.
     1174 * @param string $object_subtype Optional. The subtype of the object type.
     1175 * @return bool True if successful. False if the meta key was not registered.
     1176 */
     1177function unregister_meta_key( $object_type, $meta_key, $object_subtype = '' ) {
    11091178    global $wp_meta_keys;
    11101179
    1111     if ( ! is_array( $wp_meta_keys ) ) {
    1112         return false;
    1113     }
    1114 
    1115     if ( ! isset( $wp_meta_keys[ $object_type ] ) ) {
    1116         return false;
    1117     }
    1118 
    1119     if ( isset( $wp_meta_keys[ $object_type ][ $meta_key ] ) ) {
    1120         return true;
    1121     }
    1122 
    1123     return false;
    1124 }
    1125 
    1126 /**
    1127  * Unregisters a meta key from the list of registered keys.
    1128  *
    1129  * @since 4.6.0
    1130  *
    1131  * @param string $object_type The type of object.
    1132  * @param string $meta_key    The meta key.
    1133  * @return bool True if successful. False if the meta key was not registered.
    1134  */
    1135 function unregister_meta_key( $object_type, $meta_key ) {
    1136     global $wp_meta_keys;
    1137 
    1138     if ( ! registered_meta_key_exists( $object_type, $meta_key ) ) {
    1139         return false;
    1140     }
    1141 
    1142     $args = $wp_meta_keys[ $object_type ][ $meta_key ];
     1180    if ( ! registered_meta_key_exists( $object_type, $meta_key, $object_subtype ) ) {
     1181        return false;
     1182    }
     1183
     1184    $args = $wp_meta_keys[ $object_type ][ $object_subtype ][ $meta_key ];
    11431185
    11441186    if ( isset( $args['sanitize_callback'] ) && is_callable( $args['sanitize_callback'] ) ) {
    1145         remove_filter( "sanitize_{$object_type}_meta_{$meta_key}", $args['sanitize_callback'] );
     1187        if ( ! empty( $object_subtype ) ) {
     1188            remove_filter( "sanitize_{$object_type}_meta_{$meta_key}_for_{$object_subtype}", $args['sanitize_callback'] );
     1189        } else {
     1190            remove_filter( "sanitize_{$object_type}_meta_{$meta_key}", $args['sanitize_callback'] );
     1191        }
    11461192    }
    11471193
    11481194    if ( isset( $args['auth_callback'] ) && is_callable( $args['auth_callback'] ) ) {
    1149         remove_filter( "auth_{$object_type}_meta_{$meta_key}", $args['auth_callback'] );
    1150     }
    1151 
    1152     unset( $wp_meta_keys[ $object_type ][ $meta_key ] );
     1195        if ( ! empty( $object_subtype ) ) {
     1196            remove_filter( "auth_{$object_type}_meta_{$meta_key}_for_{$object_subtype}", $args['auth_callback'] );
     1197        } else {
     1198            remove_filter( "auth_{$object_type}_meta_{$meta_key}", $args['auth_callback'] );
     1199        }
     1200    }
     1201
     1202    unset( $wp_meta_keys[ $object_type ][ $object_subtype ][ $meta_key ] );
    11531203
    11541204    // Do some clean up
     1205    if ( empty( $wp_meta_keys[ $object_type ][ $object_subtype ] ) ) {
     1206        unset( $wp_meta_keys[ $object_type ][ $object_subtype ] );
     1207    }
    11551208    if ( empty( $wp_meta_keys[ $object_type ] ) ) {
    11561209        unset( $wp_meta_keys[ $object_type ] );
     
    11641217 *
    11651218 * @since 4.6.0
    1166  *
    1167  * @param string $object_type The type of object. Post, comment, user, term.
     1219 * @since 5.0.0 The `$object_subtype` parameter was added.
     1220 *
     1221 * @param string $object_type    The type of object. Post, comment, user, term.
     1222 * @param string $object_subtype Optional. The subtype of the object type.
    11681223 * @return array List of registered meta keys.
    11691224 */
    1170 function get_registered_meta_keys( $object_type ) {
     1225function get_registered_meta_keys( $object_type, $object_subtype = '' ) {
    11711226    global $wp_meta_keys;
    11721227
    1173     if ( ! is_array( $wp_meta_keys ) || ! isset( $wp_meta_keys[ $object_type ] ) ) {
     1228    if ( ! is_array( $wp_meta_keys ) || ! isset( $wp_meta_keys[ $object_type ] ) || ! isset( $wp_meta_keys[ $object_type ][ $object_subtype ] ) ) {
    11741229        return array();
    11751230    }
    11761231
    1177     return $wp_meta_keys[ $object_type ];
     1232    return $wp_meta_keys[ $object_type ][ $object_subtype ];
    11781233}
    11791234
    11801235/**
    11811236 * Retrieves registered metadata for a specified object.
     1237 *
     1238 * The results include both meta that is registered specifically for the
     1239 * object's subtype and meta that is registered for the entire object type.
    11821240 *
    11831241 * @since 4.6.0
     
    11881246 *                            metadata for the specified object.
    11891247 * @return mixed A single value or array of values for a key if specified. An array of all registered keys
    1190  *               and values for an object ID if not.
     1248 *               and values for an object ID if not. False if a given $meta_key is not registered.
    11911249 */
    11921250function get_registered_metadata( $object_type, $object_id, $meta_key = '' ) {
     1251    $object_subtype = get_object_subtype( $object_type, $object_id );
     1252
    11931253    if ( ! empty( $meta_key ) ) {
    1194         if ( ! registered_meta_key_exists( $object_type, $meta_key ) ) {
     1254        if ( ! empty( $object_subtype ) && ! registered_meta_key_exists( $object_type, $meta_key, $object_subtype ) ) {
     1255            $object_subtype = '';
     1256        }
     1257
     1258        if ( ! registered_meta_key_exists( $object_type, $meta_key, $object_subtype ) ) {
    11951259            return false;
    11961260        }
    1197         $meta_keys     = get_registered_meta_keys( $object_type );
     1261
     1262        $meta_keys     = get_registered_meta_keys( $object_type, $object_subtype );
    11981263        $meta_key_data = $meta_keys[ $meta_key ];
    11991264
     
    12041269
    12051270    $data = get_metadata( $object_type, $object_id );
    1206 
    1207     $meta_keys       = get_registered_meta_keys( $object_type );
    1208     $registered_data = array();
    1209 
    1210     // Someday, array_filter()
    1211     foreach ( $meta_keys as $k => $v ) {
    1212         if ( isset( $data[ $k ] ) ) {
    1213             $registered_data[ $k ] = $data[ $k ];
    1214         }
    1215     }
    1216 
    1217     return $registered_data;
     1271    if ( ! $data ) {
     1272        return array();
     1273    }
     1274
     1275    $meta_keys = get_registered_meta_keys( $object_type );
     1276    if ( ! empty( $object_subtype ) ) {
     1277        $meta_keys = array_merge( $meta_keys, get_registered_meta_keys( $object_type, $object_subtype ) );
     1278    }
     1279
     1280    return array_intersect_key( $data, $meta_keys );
    12181281}
    12191282
     
    12241287 *
    12251288 * @access private
    1226  * @since  4.6.0
    1227  *
    1228  * @param  array $args         Arguments from `register_meta()`.
    1229  * @param  array $default_args Default arguments for `register_meta()`.
     1289 * @since 4.6.0
     1290 *
     1291 * @param array $args         Arguments from `register_meta()`.
     1292 * @param array $default_args Default arguments for `register_meta()`.
    12301293 *
    12311294 * @return array Filtered arguments.
    12321295 */
    12331296function _wp_register_meta_args_whitelist( $args, $default_args ) {
    1234     $whitelist = array_keys( $default_args );
    1235 
    1236     // In an anonymous function world, this would be better as an array_filter()
    1237     foreach ( $args as $key => $value ) {
    1238         if ( ! in_array( $key, $whitelist ) ) {
    1239             unset( $args[ $key ] );
    1240         }
    1241     }
    1242 
    1243     return $args;
    1244 }
     1297    return array_intersect_key( $args, $default_args );
     1298}
     1299
     1300/**
     1301 * Returns the object subtype for a given object ID of a specific type.
     1302 *
     1303 * @since 5.0.0
     1304 *
     1305 * @param string $object_type Type of object to request metadata for. (e.g. comment, post, term, user)
     1306 * @param int    $object_id   ID of the object to retrieve its subtype.
     1307 * @return string The object subtype or an empty string if unspecified subtype.
     1308 */
     1309function get_object_subtype( $object_type, $object_id ) {
     1310    $object_id      = (int) $object_id;
     1311    $object_subtype = '';
     1312
     1313    switch ( $object_type ) {
     1314        case 'post':
     1315            $post_type = get_post_type( $object_id );
     1316
     1317            if ( ! empty( $post_type ) ) {
     1318                $object_subtype = $post_type;
     1319            }
     1320            break;
     1321
     1322        case 'term':
     1323            $term = get_term( $object_id );
     1324            if ( ! $term instanceof WP_Term ) {
     1325                break;
     1326            }
     1327
     1328            $object_subtype = $term->taxonomy;
     1329            break;
     1330
     1331        case 'comment':
     1332            $comment = get_comment( $object_id );
     1333            if ( ! $comment ) {
     1334                break;
     1335            }
     1336
     1337            $object_subtype = 'comment';
     1338            break;
     1339
     1340        case 'user':
     1341            $user = get_user_by( 'id', $object_id );
     1342            if ( ! $user ) {
     1343                break;
     1344            }
     1345
     1346            $object_subtype = 'user';
     1347            break;
     1348    }
     1349
     1350    /**
     1351     * Filters the object subtype identifier for a non standard object type.
     1352     *
     1353     * The dynamic portion of the hook, `$object_type`, refers to the object
     1354     * type (post, comment, term, or user).
     1355     *
     1356     * @since 5.0.0
     1357     *
     1358     * @param string $object_subtype Empty string to override.
     1359     * @param int    $object_id      ID of the object to get the subtype for.
     1360     */
     1361    return apply_filters( "get_object_subtype_{$object_type}", $object_subtype, $object_id );
     1362}
Note: See TracChangeset for help on using the changeset viewer.