$search Search string. * @param string[] $columns Array of columns to search. * @return string Search SQL. */ protected function get_search_sql( $search, $columns ) { global $wpdb; $like = '%' . $wpdb->esc_like( $search ) . '%'; $searches = array(); foreach ( $columns as $column ) { $searches[] = $wpdb->prepare( "$column LIKE %s", $like ); } return ' AND (' . implode( ' OR ', $searches ) . ')'; } /** * Parse and sanitize 'orderby' keys passed to the comment query. * * @since 4.2.0 * * @global wpdb $wpdb WordPress database abstraction object. * * @param string $orderby Alias for the field to order by. * @return string|false Value to used in the ORDER clause. False otherwise. */ protected function parse_orderby( $orderby ) { global $wpdb; $allowed_keys = array( 'comment_agent', 'comment_approved', 'comment_author', 'comment_author_email', 'comment_author_IP', 'comment_author_url', 'comment_content', 'comment_date', 'comment_date_gmt', 'comment_ID', 'comment_karma', 'comment_parent', 'comment_post_ID', 'comment_type', 'user_id', ); if ( ! empty( $this->query_vars['meta_key'] ) ) { $allowed_keys[] = $this->query_vars['meta_key']; $allowed_keys[] = 'meta_value'; $allowed_keys[] = 'meta_value_num'; } $meta_query_clauses = $this->meta_query->get_clauses(); if ( $meta_query_clauses ) { $allowed_keys = array_merge( $allowed_keys, array_keys( $meta_query_clauses ) ); } $parsed = false; if ( $this->query_vars['meta_key'] === $orderby || 'meta_value' === $orderby ) { $parsed = "$wpdb->commentmeta.meta_value"; } elseif ( 'meta_value_num' === $orderby ) { $parsed = "$wpdb->commentmeta.meta_value+0"; } elseif ( 'comment__in' === $orderby ) { $comment__in = implode( ',', array_map( 'absint', $this->query_vars['comment__in'] ) ); $parsed = "FIELD( {$wpdb->comments}.comment_ID, $comment__in )"; } elseif ( in_array( $orderby, $allowed_keys, true ) ) { if ( isset( $meta_query_clauses[ $orderby ] ) ) { $meta_clause = $meta_query_clauses[ $orderby ]; $parsed = sprintf( 'CAST(%s.meta_value AS %s)', esc_sql( $meta_clause['alias'] ), esc_sql( $meta_clause['cast'] ) ); } else { $parsed = "$wpdb->comments.$orderby"; } } return $parsed; } /** * Parse an 'order' query variable and cast it to ASC or DESC as necessary. * * @since 4.2.0 * * @param string $order The 'order' query variable. * @return string The sanitized 'order' query variable. */ protected function parse_order( $order ) { if ( ! is_string( $order ) || empty( $order ) ) { return 'DESC'; } if ( 'ASC' === strtoupper( $order ) ) { return 'ASC'; } else { return 'DESC'; } } } = $this->get_registered_fields()[ $meta_key ]; $sanitized = sanitize_meta( $meta_key, $user_value, $this->get_meta_type(), $subtype ); if ( in_array( $args['type'], array( 'string', 'number', 'integer', 'boolean' ), true ) ) { // The return value of get_metadata will always be a string for scalar types. $sanitized = (string) $sanitized; } return $sanitized === $stored_value; } /** * Retrieves all the registered meta fields. * * @since 4.7.0 * * @return array Registered fields. */ protected function get_registered_fields() { $registered = array(); $meta_type = $this->get_meta_type(); $meta_subtype = $this->get_meta_subtype(); $meta_keys = get_registered_meta_keys( $meta_type ); if ( ! empty( $meta_subtype ) ) { $meta_keys = array_merge( $meta_keys, get_registered_meta_keys( $meta_type, $meta_subtype ) ); } foreach ( $meta_keys as $name => $args ) { if ( empty( $args['show_in_rest'] ) ) { continue; } $rest_args = array(); if ( is_array( $args['show_in_rest'] ) ) { $rest_args = $args['show_in_rest']; } $default_args = array( 'name' => $name, 'single' => $args['single'], 'type' => ! empty( $args['type'] ) ? $args['type'] : null, 'schema' => array(), 'prepare_callback' => array( $this, 'prepare_value' ), ); $default_schema = array( 'type' => $default_args['type'], 'description' => empty( $args['description'] ) ? '' : $args['description'], 'default' => isset( $args['default'] ) ? $args['default'] : null, ); $rest_args = array_merge( $default_args, $rest_args ); $rest_args['schema'] = array_merge( $default_schema, $rest_args['schema'] ); $type = ! empty( $rest_args['type'] ) ? $rest_args['type'] : null; $type = ! empty( $rest_args['schema']['type'] ) ? $rest_args['schema']['type'] : $type; if ( null === $rest_args['schema']['default'] ) { $rest_args['schema']['default'] = static::get_empty_value_for_type( $type ); } $rest_args['schema'] = rest_default_additional_properties_to_false( $rest_args['schema'] ); if ( ! in_array( $type, array( 'string', 'boolean', 'integer', 'number', 'array', 'object' ), true ) ) { continue; } if ( empty( $rest_args['single'] ) ) { $rest_args['schema'] = array( 'type' => 'array', 'items' => $rest_args['schema'], ); } $registered[ $name ] = $rest_args; } return $registered; } /** * Retrieves the object's meta schema, conforming to JSON Schema. * * @since 4.7.0 * * @return array Field schema data. */ public function get_field_schema() { $fields = $this->get_registered_fields(); $schema = array( 'description' => __( 'Meta fields.' ), 'type' => 'object', 'context' => array( 'view', 'edit' ), 'properties' => array(), 'arg_options' => array( 'sanitize_callback' => null, 'validate_callback' => array( $this, 'check_meta_is_array' ), ), ); foreach ( $fields as $args ) { $schema['properties'][ $args['name'] ] = $args['schema']; } return $schema; } /** * Prepares a meta value for output. * * Default preparation for meta fields. Override by passing the * `prepare_callback` in your `show_in_rest` options. * * @since 4.7.0 * * @param mixed $value Meta value from the database. * @param WP_REST_Request $request Request object. * @param array $args REST-specific options for the meta key. * @return mixed Value prepared for output. If a non-JsonSerializable object, null. */ public static function prepare_value( $value, $request, $args ) { if ( $args['single'] ) { $schema = $args['schema']; } else { $schema = $args['schema']['items']; } if ( '' === $value && in_array( $schema['type'], array( 'boolean', 'integer', 'number' ), true ) ) { $value = static::get_empty_value_for_type( $schema['type'] ); } if ( is_wp_error( rest_validate_value_from_schema( $value, $schema ) ) ) { return null; } return rest_sanitize_value_from_schema( $value, $schema ); } /** * Check the 'meta' value of a request is an associative array. * * @since 4.7.0 * * @param mixed $value The meta value submitted in the request. * @param WP_REST_Request $request Full details about the request. * @param string $param The parameter name. * @return array|false The meta array, if valid, false otherwise. */ public function check_meta_is_array( $value, $request, $param ) { if ( ! is_array( $value ) ) { return false; } return $value; } /** * Recursively add additionalProperties = false to all objects in a schema if no additionalProperties setting * is specified. * * This is needed to restrict properties of objects in meta values to only * registered items, as the REST API will allow additional properties by * default. * * @since 5.3.0 * @deprecated 5.6.0 Use rest_default_additional_properties_to_false() instead. * * @param array $schema The schema array. * @return array */ protected function default_additional_properties_to_false( $schema ) { _deprecated_function( __METHOD__, '5.6.0', 'rest_default_additional_properties_to_false()' ); return rest_default_additional_properties_to_false( $schema ); } /** * Gets the empty value for a schema type. * * @since 5.3.0 * * @param string $type The schema type. * @return mixed */ protected static function get_empty_value_for_type( $type ) { switch ( $type ) { case 'string': return ''; case 'boolean': return false; case 'integer': return 0; case 'number': return 0.0; case 'array': case 'object': return array(); default: return null; } } }