Changeset 49257 for trunk/src/wp-includes/rest-api.php
- Timestamp:
- 10/20/2020 08:17:20 PM (4 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/wp-includes/rest-api.php
r49246 r49257 1876 1876 1877 1877 /** 1878 * Validate a value based on a schema. 1879 * 1880 * @since 4.7.0 1881 * @since 4.9.0 Support the "object" type. 1882 * @since 5.2.0 Support validating "additionalProperties" against a schema. 1883 * @since 5.3.0 Support multiple types. 1884 * @since 5.4.0 Convert an empty string to an empty object. 1885 * @since 5.5.0 Add the "uuid" and "hex-color" formats. 1886 * Support the "minLength", "maxLength" and "pattern" keywords for strings. 1887 * Support the "minItems", "maxItems" and "uniqueItems" keywords for arrays. 1888 * Validate required properties. 1889 * @since 5.6.0 Support the "minProperties" and "maxProperties" keywords for objects. 1890 * Support the "multipleOf" keyword for numbers and integers. 1891 * Support the "patternProperties" keyword for objects. 1892 * Support the "anyOf" and "oneOf" keywords. 1893 * 1894 * @param mixed $value The value to validate. 1895 * @param array $args Schema array to use for validation. 1896 * @param string $param The parameter name, used in error messages. 1897 * @return true|WP_Error 1898 */ 1899 function rest_validate_value_from_schema( $value, $args, $param = '' ) { 1900 if ( isset( $args['anyOf'] ) ) { 1901 $matching_schema = rest_find_any_matching_schema( $value, $args, $param ); 1902 if ( is_wp_error( $matching_schema ) ) { 1903 return $matching_schema; 1904 } 1905 1906 if ( ! isset( $args['type'] ) && isset( $matching_schema['type'] ) ) { 1907 $args['type'] = $matching_schema['type']; 1908 } 1909 } 1910 1911 if ( isset( $args['oneOf'] ) ) { 1912 $matching_schema = rest_find_one_matching_schema( $value, $args, $param ); 1913 if ( is_wp_error( $matching_schema ) ) { 1914 return $matching_schema; 1915 } 1916 1917 if ( ! isset( $args['type'] ) && isset( $matching_schema['type'] ) ) { 1918 $args['type'] = $matching_schema['type']; 1919 } 1920 } 1921 1922 $allowed_types = array( 'array', 'object', 'string', 'number', 'integer', 'boolean', 'null' ); 1923 1924 if ( ! isset( $args['type'] ) ) { 1925 /* translators: %s: Parameter. */ 1926 _doing_it_wrong( __FUNCTION__, sprintf( __( 'The "type" schema keyword for %s is required.' ), $param ), '5.5.0' ); 1927 } 1928 1929 if ( is_array( $args['type'] ) ) { 1930 $best_type = rest_handle_multi_type_schema( $value, $args, $param ); 1931 1932 if ( ! $best_type ) { 1933 return new WP_Error( 1934 'rest_invalid_type', 1935 /* translators: 1: Parameter, 2: List of types. */ 1936 sprintf( __( '%1$s is not of type %2$s.' ), $param, implode( ',', $args['type'] ) ), 1937 array( 'param' => $param ) 1938 ); 1939 } 1940 1941 $args['type'] = $best_type; 1942 } 1943 1944 if ( ! in_array( $args['type'], $allowed_types, true ) ) { 1945 _doing_it_wrong( 1946 __FUNCTION__, 1947 /* translators: 1: Parameter, 2: The list of allowed types. */ 1948 wp_sprintf( __( 'The "type" schema keyword for %1$s can only be one of the built-in types: %2$l.' ), $param, $allowed_types ), 1949 '5.5.0' 1950 ); 1951 } 1952 1953 if ( 'array' === $args['type'] ) { 1954 if ( ! rest_is_array( $value ) ) { 1955 return new WP_Error( 1956 'rest_invalid_type', 1957 /* translators: 1: Parameter, 2: Type name. */ 1958 sprintf( __( '%1$s is not of type %2$s.' ), $param, 'array' ), 1959 array( 'param' => $param ) 1960 ); 1961 } 1962 1963 $value = rest_sanitize_array( $value ); 1964 1965 if ( isset( $args['items'] ) ) { 1966 foreach ( $value as $index => $v ) { 1967 $is_valid = rest_validate_value_from_schema( $v, $args['items'], $param . '[' . $index . ']' ); 1968 if ( is_wp_error( $is_valid ) ) { 1969 return $is_valid; 1970 } 1971 } 1972 } 1973 1974 if ( isset( $args['minItems'] ) && count( $value ) < $args['minItems'] ) { 1975 /* translators: 1: Parameter, 2: Number. */ 1976 return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must contain at least %2$s items.' ), $param, number_format_i18n( $args['minItems'] ) ) ); 1977 } 1978 1979 if ( isset( $args['maxItems'] ) && count( $value ) > $args['maxItems'] ) { 1980 /* translators: 1: Parameter, 2: Number. */ 1981 return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must contain at most %2$s items.' ), $param, number_format_i18n( $args['maxItems'] ) ) ); 1982 } 1983 1984 if ( ! empty( $args['uniqueItems'] ) && ! rest_validate_array_contains_unique_items( $value ) ) { 1985 /* translators: 1: Parameter. */ 1986 return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s has duplicate items.' ), $param ) ); 1987 } 1988 } 1989 1990 if ( 'object' === $args['type'] ) { 1991 if ( ! rest_is_object( $value ) ) { 1992 return new WP_Error( 1993 'rest_invalid_type', 1994 /* translators: 1: Parameter, 2: Type name. */ 1995 sprintf( __( '%1$s is not of type %2$s.' ), $param, 'object' ), 1996 array( 'param' => $param ) 1997 ); 1998 } 1999 2000 $value = rest_sanitize_object( $value ); 2001 2002 if ( isset( $args['required'] ) && is_array( $args['required'] ) ) { // schema version 4 2003 foreach ( $args['required'] as $name ) { 2004 if ( ! array_key_exists( $name, $value ) ) { 2005 /* translators: 1: Property of an object, 2: Parameter. */ 2006 return new WP_Error( 'rest_property_required', sprintf( __( '%1$s is a required property of %2$s.' ), $name, $param ) ); 2007 } 2008 } 2009 } elseif ( isset( $args['properties'] ) ) { // schema version 3 2010 foreach ( $args['properties'] as $name => $property ) { 2011 if ( isset( $property['required'] ) && true === $property['required'] && ! array_key_exists( $name, $value ) ) { 2012 /* translators: 1: Property of an object, 2: Parameter. */ 2013 return new WP_Error( 'rest_property_required', sprintf( __( '%1$s is a required property of %2$s.' ), $name, $param ) ); 2014 } 2015 } 2016 } 2017 2018 foreach ( $value as $property => $v ) { 2019 if ( isset( $args['properties'][ $property ] ) ) { 2020 $is_valid = rest_validate_value_from_schema( $v, $args['properties'][ $property ], $param . '[' . $property . ']' ); 2021 if ( is_wp_error( $is_valid ) ) { 2022 return $is_valid; 2023 } 2024 continue; 2025 } 2026 2027 $pattern_property_schema = rest_find_matching_pattern_property_schema( $property, $args ); 2028 if ( null !== $pattern_property_schema ) { 2029 $is_valid = rest_validate_value_from_schema( $v, $pattern_property_schema, $param . '[' . $property . ']' ); 2030 if ( is_wp_error( $is_valid ) ) { 2031 return $is_valid; 2032 } 2033 continue; 2034 } 2035 2036 if ( isset( $args['additionalProperties'] ) ) { 2037 if ( false === $args['additionalProperties'] ) { 2038 /* translators: %s: Property of an object. */ 2039 return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s is not a valid property of Object.' ), $property ) ); 2040 } 2041 2042 if ( is_array( $args['additionalProperties'] ) ) { 2043 $is_valid = rest_validate_value_from_schema( $v, $args['additionalProperties'], $param . '[' . $property . ']' ); 2044 if ( is_wp_error( $is_valid ) ) { 2045 return $is_valid; 2046 } 2047 } 2048 } 2049 } 2050 2051 if ( isset( $args['minProperties'] ) && count( $value ) < $args['minProperties'] ) { 2052 /* translators: 1: Parameter, 2: Number. */ 2053 return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must contain at least %2$s properties.' ), $param, number_format_i18n( $args['minProperties'] ) ) ); 2054 } 2055 2056 if ( isset( $args['maxProperties'] ) && count( $value ) > $args['maxProperties'] ) { 2057 /* translators: 1: Parameter, 2: Number. */ 2058 return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must contain at most %2$s properties.' ), $param, number_format_i18n( $args['maxProperties'] ) ) ); 2059 } 2060 } 2061 2062 if ( 'null' === $args['type'] ) { 2063 if ( null !== $value ) { 2064 return new WP_Error( 2065 'rest_invalid_type', 2066 /* translators: 1: Parameter, 2: Type name. */ 2067 sprintf( __( '%1$s is not of type %2$s.' ), $param, 'null' ), 2068 array( 'param' => $param ) 2069 ); 2070 } 2071 2072 return true; 2073 } 2074 2075 if ( ! empty( $args['enum'] ) ) { 2076 if ( ! in_array( $value, $args['enum'], true ) ) { 2077 /* translators: 1: Parameter, 2: List of valid values. */ 2078 return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s is not one of %2$s.' ), $param, implode( ', ', $args['enum'] ) ) ); 2079 } 2080 } 2081 2082 if ( in_array( $args['type'], array( 'integer', 'number' ), true ) ) { 2083 if ( ! is_numeric( $value ) ) { 2084 return new WP_Error( 2085 'rest_invalid_type', 2086 /* translators: 1: Parameter, 2: Type name. */ 2087 sprintf( __( '%1$s is not of type %2$s.' ), $param, $args['type'] ), 2088 array( 'param' => $param ) 2089 ); 2090 } 2091 2092 if ( isset( $args['multipleOf'] ) && fmod( $value, $args['multipleOf'] ) !== 0.0 ) { 2093 /* translators: 1: Parameter, 2: Multiplier. */ 2094 return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be a multiple of %2$s.' ), $param, $args['multipleOf'] ) ); 2095 } 2096 } 2097 2098 if ( 'integer' === $args['type'] && ! rest_is_integer( $value ) ) { 2099 return new WP_Error( 2100 'rest_invalid_type', 2101 /* translators: 1: Parameter, 2: Type name. */ 2102 sprintf( __( '%1$s is not of type %2$s.' ), $param, 'integer' ), 2103 array( 'param' => $param ) 2104 ); 2105 } 2106 2107 if ( 'boolean' === $args['type'] && ! rest_is_boolean( $value ) ) { 2108 return new WP_Error( 2109 'rest_invalid_type', 2110 /* translators: 1: Parameter, 2: Type name. */ 2111 sprintf( __( '%1$s is not of type %2$s.' ), $param, 'boolean' ), 2112 array( 'param' => $param ) 2113 ); 2114 } 2115 2116 if ( 'string' === $args['type'] ) { 2117 if ( ! is_string( $value ) ) { 2118 return new WP_Error( 2119 'rest_invalid_type', 2120 /* translators: 1: Parameter, 2: Type name. */ 2121 sprintf( __( '%1$s is not of type %2$s.' ), $param, 'string' ), 2122 array( 'param' => $param ) 2123 ); 2124 } 2125 2126 if ( isset( $args['minLength'] ) && mb_strlen( $value ) < $args['minLength'] ) { 2127 return new WP_Error( 2128 'rest_invalid_param', 2129 sprintf( 2130 /* translators: 1: Parameter, 2: Number of characters. */ 2131 _n( '%1$s must be at least %2$s character long.', '%1$s must be at least %2$s characters long.', $args['minLength'] ), 2132 $param, 2133 number_format_i18n( $args['minLength'] ) 2134 ) 2135 ); 2136 } 2137 2138 if ( isset( $args['maxLength'] ) && mb_strlen( $value ) > $args['maxLength'] ) { 2139 return new WP_Error( 2140 'rest_invalid_param', 2141 sprintf( 2142 /* translators: 1: Parameter, 2: Number of characters. */ 2143 _n( '%1$s must be at most %2$s character long.', '%1$s must be at most %2$s characters long.', $args['maxLength'] ), 2144 $param, 2145 number_format_i18n( $args['maxLength'] ) 2146 ) 2147 ); 2148 } 2149 2150 if ( isset( $args['pattern'] ) && ! rest_validate_json_schema_pattern( $args['pattern'], $value ) ) { 2151 /* translators: 1: Parameter, 2: Pattern. */ 2152 return new WP_Error( 'rest_invalid_pattern', sprintf( __( '%1$s does not match pattern %2$s.' ), $param, $args['pattern'] ) ); 2153 } 2154 } 2155 2156 // The "format" keyword should only be applied to strings. However, for backward compatibility, 2157 // we allow the "format" keyword if the type keyword was not specified, or was set to an invalid value. 2158 if ( isset( $args['format'] ) 2159 && ( ! isset( $args['type'] ) || 'string' === $args['type'] || ! in_array( $args['type'], $allowed_types, true ) ) 2160 ) { 2161 switch ( $args['format'] ) { 2162 case 'hex-color': 2163 if ( ! rest_parse_hex_color( $value ) ) { 2164 return new WP_Error( 'rest_invalid_hex_color', __( 'Invalid hex color.' ) ); 2165 } 2166 break; 2167 2168 case 'date-time': 2169 if ( ! rest_parse_date( $value ) ) { 2170 return new WP_Error( 'rest_invalid_date', __( 'Invalid date.' ) ); 2171 } 2172 break; 2173 2174 case 'email': 2175 if ( ! is_email( $value ) ) { 2176 return new WP_Error( 'rest_invalid_email', __( 'Invalid email address.' ) ); 2177 } 2178 break; 2179 case 'ip': 2180 if ( ! rest_is_ip_address( $value ) ) { 2181 /* translators: %s: IP address. */ 2182 return new WP_Error( 'rest_invalid_param', sprintf( __( '%s is not a valid IP address.' ), $param ) ); 2183 } 2184 break; 2185 case 'uuid': 2186 if ( ! wp_is_uuid( $value ) ) { 2187 /* translators: %s: The name of a JSON field expecting a valid UUID. */ 2188 return new WP_Error( 'rest_invalid_uuid', sprintf( __( '%s is not a valid UUID.' ), $param ) ); 2189 } 2190 break; 2191 } 2192 } 2193 2194 if ( in_array( $args['type'], array( 'number', 'integer' ), true ) && ( isset( $args['minimum'] ) || isset( $args['maximum'] ) ) ) { 2195 if ( isset( $args['minimum'] ) && ! isset( $args['maximum'] ) ) { 2196 if ( ! empty( $args['exclusiveMinimum'] ) && $value <= $args['minimum'] ) { 2197 /* translators: 1: Parameter, 2: Minimum number. */ 2198 return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be greater than %2$d' ), $param, $args['minimum'] ) ); 2199 } elseif ( empty( $args['exclusiveMinimum'] ) && $value < $args['minimum'] ) { 2200 /* translators: 1: Parameter, 2: Minimum number. */ 2201 return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be greater than or equal to %2$d' ), $param, $args['minimum'] ) ); 2202 } 2203 } elseif ( isset( $args['maximum'] ) && ! isset( $args['minimum'] ) ) { 2204 if ( ! empty( $args['exclusiveMaximum'] ) && $value >= $args['maximum'] ) { 2205 /* translators: 1: Parameter, 2: Maximum number. */ 2206 return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be less than %2$d' ), $param, $args['maximum'] ) ); 2207 } elseif ( empty( $args['exclusiveMaximum'] ) && $value > $args['maximum'] ) { 2208 /* translators: 1: Parameter, 2: Maximum number. */ 2209 return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be less than or equal to %2$d' ), $param, $args['maximum'] ) ); 2210 } 2211 } elseif ( isset( $args['maximum'] ) && isset( $args['minimum'] ) ) { 2212 if ( ! empty( $args['exclusiveMinimum'] ) && ! empty( $args['exclusiveMaximum'] ) ) { 2213 if ( $value >= $args['maximum'] || $value <= $args['minimum'] ) { 2214 /* translators: 1: Parameter, 2: Minimum number, 3: Maximum number. */ 2215 return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be between %2$d (exclusive) and %3$d (exclusive)' ), $param, $args['minimum'], $args['maximum'] ) ); 2216 } 2217 } elseif ( empty( $args['exclusiveMinimum'] ) && ! empty( $args['exclusiveMaximum'] ) ) { 2218 if ( $value >= $args['maximum'] || $value < $args['minimum'] ) { 2219 /* translators: 1: Parameter, 2: Minimum number, 3: Maximum number. */ 2220 return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be between %2$d (inclusive) and %3$d (exclusive)' ), $param, $args['minimum'], $args['maximum'] ) ); 2221 } 2222 } elseif ( ! empty( $args['exclusiveMinimum'] ) && empty( $args['exclusiveMaximum'] ) ) { 2223 if ( $value > $args['maximum'] || $value <= $args['minimum'] ) { 2224 /* translators: 1: Parameter, 2: Minimum number, 3: Maximum number. */ 2225 return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be between %2$d (exclusive) and %3$d (inclusive)' ), $param, $args['minimum'], $args['maximum'] ) ); 2226 } 2227 } elseif ( empty( $args['exclusiveMinimum'] ) && empty( $args['exclusiveMaximum'] ) ) { 2228 if ( $value > $args['maximum'] || $value < $args['minimum'] ) { 2229 /* translators: 1: Parameter, 2: Minimum number, 3: Maximum number. */ 2230 return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be between %2$d (inclusive) and %3$d (inclusive)' ), $param, $args['minimum'], $args['maximum'] ) ); 2231 } 2232 } 2233 } 2234 } 2235 2236 return true; 2237 } 2238 2239 /** 2240 * Sanitize a value based on a schema. 2241 * 2242 * @since 4.7.0 2243 * @since 5.5.0 Added the `$param` parameter. 2244 * @since 5.6.0 Support the "anyOf" and "oneOf" keywords. 2245 * 2246 * @param mixed $value The value to sanitize. 2247 * @param array $args Schema array to use for sanitization. 2248 * @param string $param The parameter name, used in error messages. 2249 * @return mixed|WP_Error The sanitized value or a WP_Error instance if the value cannot be safely sanitized. 2250 */ 2251 function rest_sanitize_value_from_schema( $value, $args, $param = '' ) { 2252 if ( isset( $args['anyOf'] ) ) { 2253 $matching_schema = rest_find_any_matching_schema( $value, $args, $param ); 2254 if ( is_wp_error( $matching_schema ) ) { 2255 return $matching_schema; 2256 } 2257 2258 if ( ! isset( $args['type'] ) ) { 2259 $args['type'] = $matching_schema['type']; 2260 } 2261 2262 $value = rest_sanitize_value_from_schema( $value, $matching_schema, $param ); 2263 } 2264 2265 if ( isset( $args['oneOf'] ) ) { 2266 $matching_schema = rest_find_one_matching_schema( $value, $args, $param ); 2267 if ( is_wp_error( $matching_schema ) ) { 2268 return $matching_schema; 2269 } 2270 2271 if ( ! isset( $args['type'] ) ) { 2272 $args['type'] = $matching_schema['type']; 2273 } 2274 2275 $value = rest_sanitize_value_from_schema( $value, $matching_schema, $param ); 2276 } 2277 2278 $allowed_types = array( 'array', 'object', 'string', 'number', 'integer', 'boolean', 'null' ); 2279 2280 if ( ! isset( $args['type'] ) ) { 2281 /* translators: %s: Parameter. */ 2282 _doing_it_wrong( __FUNCTION__, sprintf( __( 'The "type" schema keyword for %s is required.' ), $param ), '5.5.0' ); 2283 } 2284 2285 if ( is_array( $args['type'] ) ) { 2286 $best_type = rest_handle_multi_type_schema( $value, $args, $param ); 2287 2288 if ( ! $best_type ) { 2289 return null; 2290 } 2291 2292 $args['type'] = $best_type; 2293 } 2294 2295 if ( ! in_array( $args['type'], $allowed_types, true ) ) { 2296 _doing_it_wrong( 2297 __FUNCTION__, 2298 /* translators: 1: Parameter, 2: The list of allowed types. */ 2299 wp_sprintf( __( 'The "type" schema keyword for %1$s can only be one of the built-in types: %2$l.' ), $param, $allowed_types ), 2300 '5.5.0' 2301 ); 2302 } 2303 2304 if ( 'array' === $args['type'] ) { 2305 $value = rest_sanitize_array( $value ); 2306 2307 if ( ! empty( $args['items'] ) ) { 2308 foreach ( $value as $index => $v ) { 2309 $value[ $index ] = rest_sanitize_value_from_schema( $v, $args['items'], $param . '[' . $index . ']' ); 2310 } 2311 } 2312 2313 if ( ! empty( $args['uniqueItems'] ) && ! rest_validate_array_contains_unique_items( $value ) ) { 2314 /* translators: 1: Parameter. */ 2315 return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s has duplicate items.' ), $param ) ); 2316 } 2317 2318 return $value; 2319 } 2320 2321 if ( 'object' === $args['type'] ) { 2322 $value = rest_sanitize_object( $value ); 2323 2324 foreach ( $value as $property => $v ) { 2325 if ( isset( $args['properties'][ $property ] ) ) { 2326 $value[ $property ] = rest_sanitize_value_from_schema( $v, $args['properties'][ $property ], $param . '[' . $property . ']' ); 2327 continue; 2328 } 2329 2330 $pattern_property_schema = rest_find_matching_pattern_property_schema( $property, $args ); 2331 if ( null !== $pattern_property_schema ) { 2332 $value[ $property ] = rest_sanitize_value_from_schema( $v, $pattern_property_schema, $param . '[' . $property . ']' ); 2333 continue; 2334 } 2335 2336 if ( isset( $args['additionalProperties'] ) ) { 2337 if ( false === $args['additionalProperties'] ) { 2338 unset( $value[ $property ] ); 2339 } elseif ( is_array( $args['additionalProperties'] ) ) { 2340 $value[ $property ] = rest_sanitize_value_from_schema( $v, $args['additionalProperties'], $param . '[' . $property . ']' ); 2341 } 2342 } 2343 } 2344 2345 return $value; 2346 } 2347 2348 if ( 'null' === $args['type'] ) { 2349 return null; 2350 } 2351 2352 if ( 'integer' === $args['type'] ) { 2353 return (int) $value; 2354 } 2355 2356 if ( 'number' === $args['type'] ) { 2357 return (float) $value; 2358 } 2359 2360 if ( 'boolean' === $args['type'] ) { 2361 return rest_sanitize_boolean( $value ); 2362 } 2363 2364 // This behavior matches rest_validate_value_from_schema(). 2365 if ( isset( $args['format'] ) 2366 && ( ! isset( $args['type'] ) || 'string' === $args['type'] || ! in_array( $args['type'], $allowed_types, true ) ) 2367 ) { 2368 switch ( $args['format'] ) { 2369 case 'hex-color': 2370 return (string) sanitize_hex_color( $value ); 2371 2372 case 'date-time': 2373 return sanitize_text_field( $value ); 2374 2375 case 'email': 2376 // sanitize_email() validates, which would be unexpected. 2377 return sanitize_text_field( $value ); 2378 2379 case 'uri': 2380 return esc_url_raw( $value ); 2381 2382 case 'ip': 2383 return sanitize_text_field( $value ); 2384 2385 case 'uuid': 2386 return sanitize_text_field( $value ); 2387 } 2388 } 2389 2390 if ( 'string' === $args['type'] ) { 2391 return (string) $value; 2392 } 2393 2394 return $value; 2395 } 2396 2397 /** 2398 * Append result of internal request to REST API for purpose of preloading data to be attached to a page. 2399 * Expected to be called in the context of `array_reduce`. 2400 * 2401 * @since 5.0.0 2402 * 2403 * @param array $memo Reduce accumulator. 2404 * @param string $path REST API path to preload. 2405 * @return array Modified reduce accumulator. 2406 */ 2407 function rest_preload_api_request( $memo, $path ) { 2408 // array_reduce() doesn't support passing an array in PHP 5.2, 2409 // so we need to make sure we start with one. 2410 if ( ! is_array( $memo ) ) { 2411 $memo = array(); 2412 } 2413 2414 if ( empty( $path ) ) { 2415 return $memo; 2416 } 2417 2418 $method = 'GET'; 2419 if ( is_array( $path ) && 2 === count( $path ) ) { 2420 $method = end( $path ); 2421 $path = reset( $path ); 2422 2423 if ( ! in_array( $method, array( 'GET', 'OPTIONS' ), true ) ) { 2424 $method = 'GET'; 2425 } 2426 } 2427 2428 $path_parts = parse_url( $path ); 2429 if ( false === $path_parts ) { 2430 return $memo; 2431 } 2432 2433 $request = new WP_REST_Request( $method, $path_parts['path'] ); 2434 if ( ! empty( $path_parts['query'] ) ) { 2435 parse_str( $path_parts['query'], $query_params ); 2436 $request->set_query_params( $query_params ); 2437 } 2438 2439 $response = rest_do_request( $request ); 2440 if ( 200 === $response->status ) { 2441 $server = rest_get_server(); 2442 $data = (array) $response->get_data(); 2443 $links = $server::get_compact_response_links( $response ); 2444 if ( ! empty( $links ) ) { 2445 $data['_links'] = $links; 2446 } 2447 2448 if ( 'OPTIONS' === $method ) { 2449 $response = rest_send_allow_header( $response, $server, $request ); 2450 2451 $memo[ $method ][ $path ] = array( 2452 'body' => $data, 2453 'headers' => $response->headers, 2454 ); 2455 } else { 2456 $memo[ $path ] = array( 2457 'body' => $data, 2458 'headers' => $response->headers, 2459 ); 2460 } 2461 } 2462 2463 return $memo; 2464 } 2465 2466 /** 2467 * Parses the "_embed" parameter into the list of resources to embed. 2468 * 2469 * @since 5.4.0 2470 * 2471 * @param string|array $embed Raw "_embed" parameter value. 2472 * @return true|string[] Either true to embed all embeds, or a list of relations to embed. 2473 */ 2474 function rest_parse_embed_param( $embed ) { 2475 if ( ! $embed || 'true' === $embed || '1' === $embed ) { 2476 return true; 2477 } 2478 2479 $rels = wp_parse_list( $embed ); 2480 2481 if ( ! $rels ) { 2482 return true; 2483 } 2484 2485 return $rels; 2486 } 2487 2488 /** 2489 * Filters the response to remove any fields not available in the given context. 2490 * 2491 * @since 5.5.0 2492 * @since 5.6.0 Support the "patternProperties" keyword for objects. 2493 * Support the "anyOf" and "oneOf" keywords. 2494 * 2495 * @param array|object $data The response data to modify. 2496 * @param array $schema The schema for the endpoint used to filter the response. 2497 * @param string $context The requested context. 2498 * @return array|object The filtered response data. 2499 */ 2500 function rest_filter_response_by_context( $data, $schema, $context ) { 2501 if ( isset( $schema['anyOf'] ) ) { 2502 $matching_schema = rest_find_any_matching_schema( $data, $schema, '' ); 2503 if ( ! is_wp_error( $matching_schema ) ) { 2504 if ( ! isset( $schema['type'] ) ) { 2505 $schema['type'] = $matching_schema['type']; 2506 } 2507 2508 $data = rest_filter_response_by_context( $data, $matching_schema, $context ); 2509 } 2510 } 2511 2512 if ( isset( $schema['oneOf'] ) ) { 2513 $matching_schema = rest_find_one_matching_schema( $data, $schema, '', true ); 2514 if ( ! is_wp_error( $matching_schema ) ) { 2515 if ( ! isset( $schema['type'] ) ) { 2516 $schema['type'] = $matching_schema['type']; 2517 } 2518 2519 $data = rest_filter_response_by_context( $data, $matching_schema, $context ); 2520 } 2521 } 2522 2523 if ( ! is_array( $data ) && ! is_object( $data ) ) { 2524 return $data; 2525 } 2526 2527 if ( isset( $schema['type'] ) ) { 2528 $type = $schema['type']; 2529 } elseif ( isset( $schema['properties'] ) ) { 2530 $type = 'object'; // Back compat if a developer accidentally omitted the type. 2531 } else { 2532 return $data; 2533 } 2534 2535 $is_array_type = 'array' === $type || ( is_array( $type ) && in_array( 'array', $type, true ) ); 2536 $is_object_type = 'object' === $type || ( is_array( $type ) && in_array( 'object', $type, true ) ); 2537 2538 if ( $is_array_type && $is_object_type ) { 2539 if ( rest_is_array( $data ) ) { 2540 $is_object_type = false; 2541 } else { 2542 $is_array_type = false; 2543 } 2544 } 2545 2546 $has_additional_properties = $is_object_type && isset( $schema['additionalProperties'] ) && is_array( $schema['additionalProperties'] ); 2547 2548 foreach ( $data as $key => $value ) { 2549 $check = array(); 2550 2551 if ( $is_array_type ) { 2552 $check = isset( $schema['items'] ) ? $schema['items'] : array(); 2553 } elseif ( $is_object_type ) { 2554 if ( isset( $schema['properties'][ $key ] ) ) { 2555 $check = $schema['properties'][ $key ]; 2556 } else { 2557 $pattern_property_schema = rest_find_matching_pattern_property_schema( $key, $schema ); 2558 if ( null !== $pattern_property_schema ) { 2559 $check = $pattern_property_schema; 2560 } elseif ( $has_additional_properties ) { 2561 $check = $schema['additionalProperties']; 2562 } 2563 } 2564 } 2565 2566 if ( ! isset( $check['context'] ) ) { 2567 continue; 2568 } 2569 2570 if ( ! in_array( $context, $check['context'], true ) ) { 2571 if ( $is_array_type ) { 2572 // All array items share schema, so there's no need to check each one. 2573 $data = array(); 2574 break; 2575 } 2576 2577 if ( is_object( $data ) ) { 2578 unset( $data->$key ); 2579 } else { 2580 unset( $data[ $key ] ); 2581 } 2582 } elseif ( is_array( $value ) || is_object( $value ) ) { 2583 $new_value = rest_filter_response_by_context( $value, $check, $context ); 2584 2585 if ( is_object( $data ) ) { 2586 $data->$key = $new_value; 2587 } else { 2588 $data[ $key ] = $new_value; 2589 } 2590 } 2591 } 2592 2593 return $data; 2594 } 2595 2596 /** 2597 * Sets the "additionalProperties" to false by default for all object definitions in the schema. 2598 * 2599 * @since 5.5.0 2600 * @since 5.6.0 Support the "patternProperties" keyword. 2601 * 2602 * @param array $schema The schema to modify. 2603 * @return array The modified schema. 2604 */ 2605 function rest_default_additional_properties_to_false( $schema ) { 2606 $type = (array) $schema['type']; 2607 2608 if ( in_array( 'object', $type, true ) ) { 2609 if ( isset( $schema['properties'] ) ) { 2610 foreach ( $schema['properties'] as $key => $child_schema ) { 2611 $schema['properties'][ $key ] = rest_default_additional_properties_to_false( $child_schema ); 2612 } 2613 } 2614 2615 if ( isset( $schema['patternProperties'] ) ) { 2616 foreach ( $schema['patternProperties'] as $key => $child_schema ) { 2617 $schema['patternProperties'][ $key ] = rest_default_additional_properties_to_false( $child_schema ); 2618 } 2619 } 2620 2621 if ( ! isset( $schema['additionalProperties'] ) ) { 2622 $schema['additionalProperties'] = false; 2623 } 2624 } 2625 2626 if ( in_array( 'array', $type, true ) ) { 2627 if ( isset( $schema['items'] ) ) { 2628 $schema['items'] = rest_default_additional_properties_to_false( $schema['items'] ); 2629 } 2630 } 2631 2632 return $schema; 2633 } 2634 2635 /** 2636 * Gets the REST API route for a post. 2637 * 2638 * @since 5.5.0 2639 * 2640 * @param int|WP_Post $post Post ID or post object. 2641 * @return string The route path with a leading slash for the given post, or an empty string if there is not a route. 2642 */ 2643 function rest_get_route_for_post( $post ) { 2644 $post = get_post( $post ); 2645 2646 if ( ! $post instanceof WP_Post ) { 2647 return ''; 2648 } 2649 2650 $post_type = get_post_type_object( $post->post_type ); 2651 if ( ! $post_type ) { 2652 return ''; 2653 } 2654 2655 $controller = $post_type->get_rest_controller(); 2656 if ( ! $controller ) { 2657 return ''; 2658 } 2659 2660 $route = ''; 2661 2662 // The only two controllers that we can detect are the Attachments and Posts controllers. 2663 if ( in_array( get_class( $controller ), array( 'WP_REST_Attachments_Controller', 'WP_REST_Posts_Controller' ), true ) ) { 2664 $namespace = 'wp/v2'; 2665 $rest_base = ! empty( $post_type->rest_base ) ? $post_type->rest_base : $post_type->name; 2666 $route = sprintf( '/%s/%s/%d', $namespace, $rest_base, $post->ID ); 2667 } 2668 2669 /** 2670 * Filters the REST API route for a post. 2671 * 2672 * @since 5.5.0 2673 * 2674 * @param string $route The route path. 2675 * @param WP_Post $post The post object. 2676 */ 2677 return apply_filters( 'rest_route_for_post', $route, $post ); 2678 } 2679 2680 /** 2681 * Gets the REST API route for a term. 2682 * 2683 * @since 5.5.0 2684 * 2685 * @param int|WP_Term $term Term ID or term object. 2686 * @return string The route path with a leading slash for the given term, or an empty string if there is not a route. 2687 */ 2688 function rest_get_route_for_term( $term ) { 2689 $term = get_term( $term ); 2690 2691 if ( ! $term instanceof WP_Term ) { 2692 return ''; 2693 } 2694 2695 $taxonomy = get_taxonomy( $term->taxonomy ); 2696 if ( ! $taxonomy ) { 2697 return ''; 2698 } 2699 2700 $controller = $taxonomy->get_rest_controller(); 2701 if ( ! $controller ) { 2702 return ''; 2703 } 2704 2705 $route = ''; 2706 2707 // The only controller that works is the Terms controller. 2708 if ( $controller instanceof WP_REST_Terms_Controller ) { 2709 $namespace = 'wp/v2'; 2710 $rest_base = ! empty( $taxonomy->rest_base ) ? $taxonomy->rest_base : $taxonomy->name; 2711 $route = sprintf( '/%s/%s/%d', $namespace, $rest_base, $term->term_id ); 2712 } 2713 2714 /** 2715 * Filters the REST API route for a term. 2716 * 2717 * @since 5.5.0 2718 * 2719 * @param string $route The route path. 2720 * @param WP_Term $term The term object. 2721 */ 2722 return apply_filters( 'rest_route_for_term', $route, $term ); 2723 } 2724 2725 /** 2726 * Gets the REST route for the currently queried object. 2727 * 2728 * @since 5.5.0 2729 * 2730 * @return string The REST route of the resource, or an empty string if no resource identified. 2731 */ 2732 function rest_get_queried_resource_route() { 2733 if ( is_singular() ) { 2734 $route = rest_get_route_for_post( get_queried_object() ); 2735 } elseif ( is_category() || is_tag() || is_tax() ) { 2736 $route = rest_get_route_for_term( get_queried_object() ); 2737 } elseif ( is_author() ) { 2738 $route = '/wp/v2/users/' . get_queried_object_id(); 2739 } else { 2740 $route = ''; 2741 } 2742 2743 /** 2744 * Filters the REST route for the currently queried object. 2745 * 2746 * @since 5.5.0 2747 * 2748 * @param string $link The route with a leading slash, or an empty string. 2749 */ 2750 return apply_filters( 'rest_queried_resource_route', $route ); 2751 } 2752 2753 /** 2754 * Retrieves an array of endpoint arguments from the item schema and endpoint method. 1878 * Get all valid JSON schema properties. 2755 1879 * 2756 1880 * @since 5.6.0 2757 1881 * 2758 * @param array $schema The full JSON schema for the endpoint. 2759 * @param string $method Optional. HTTP method of the endpoint. The arguments for `CREATABLE` endpoints are 2760 * checked for required values and may fall-back to a given default, this is not done 2761 * on `EDITABLE` endpoints. Default WP_REST_Server::CREATABLE. 2762 * @return array The endpoint arguments. 2763 */ 2764 function rest_get_endpoint_args_for_schema( $schema, $method = WP_REST_Server::CREATABLE ) { 2765 2766 $schema_properties = ! empty( $schema['properties'] ) ? $schema['properties'] : array(); 2767 $endpoint_args = array(); 2768 $valid_schema_properties = array( 1882 * @return string[] All valid JSON schema properties. 1883 */ 1884 function rest_get_allowed_schema_keywords() { 1885 return array( 1886 'title', 1887 'description', 1888 'default', 2769 1889 'type', 2770 1890 'format', … … 2790 1910 'oneOf', 2791 1911 ); 1912 } 1913 1914 /** 1915 * Validate a value based on a schema. 1916 * 1917 * @since 4.7.0 1918 * @since 4.9.0 Support the "object" type. 1919 * @since 5.2.0 Support validating "additionalProperties" against a schema. 1920 * @since 5.3.0 Support multiple types. 1921 * @since 5.4.0 Convert an empty string to an empty object. 1922 * @since 5.5.0 Add the "uuid" and "hex-color" formats. 1923 * Support the "minLength", "maxLength" and "pattern" keywords for strings. 1924 * Support the "minItems", "maxItems" and "uniqueItems" keywords for arrays. 1925 * Validate required properties. 1926 * @since 5.6.0 Support the "minProperties" and "maxProperties" keywords for objects. 1927 * Support the "multipleOf" keyword for numbers and integers. 1928 * Support the "patternProperties" keyword for objects. 1929 * Support the "anyOf" and "oneOf" keywords. 1930 * 1931 * @param mixed $value The value to validate. 1932 * @param array $args Schema array to use for validation. 1933 * @param string $param The parameter name, used in error messages. 1934 * @return true|WP_Error 1935 */ 1936 function rest_validate_value_from_schema( $value, $args, $param = '' ) { 1937 if ( isset( $args['anyOf'] ) ) { 1938 $matching_schema = rest_find_any_matching_schema( $value, $args, $param ); 1939 if ( is_wp_error( $matching_schema ) ) { 1940 return $matching_schema; 1941 } 1942 1943 if ( ! isset( $args['type'] ) && isset( $matching_schema['type'] ) ) { 1944 $args['type'] = $matching_schema['type']; 1945 } 1946 } 1947 1948 if ( isset( $args['oneOf'] ) ) { 1949 $matching_schema = rest_find_one_matching_schema( $value, $args, $param ); 1950 if ( is_wp_error( $matching_schema ) ) { 1951 return $matching_schema; 1952 } 1953 1954 if ( ! isset( $args['type'] ) && isset( $matching_schema['type'] ) ) { 1955 $args['type'] = $matching_schema['type']; 1956 } 1957 } 1958 1959 $allowed_types = array( 'array', 'object', 'string', 'number', 'integer', 'boolean', 'null' ); 1960 1961 if ( ! isset( $args['type'] ) ) { 1962 /* translators: %s: Parameter. */ 1963 _doing_it_wrong( __FUNCTION__, sprintf( __( 'The "type" schema keyword for %s is required.' ), $param ), '5.5.0' ); 1964 } 1965 1966 if ( is_array( $args['type'] ) ) { 1967 $best_type = rest_handle_multi_type_schema( $value, $args, $param ); 1968 1969 if ( ! $best_type ) { 1970 return new WP_Error( 1971 'rest_invalid_type', 1972 /* translators: 1: Parameter, 2: List of types. */ 1973 sprintf( __( '%1$s is not of type %2$s.' ), $param, implode( ',', $args['type'] ) ), 1974 array( 'param' => $param ) 1975 ); 1976 } 1977 1978 $args['type'] = $best_type; 1979 } 1980 1981 if ( ! in_array( $args['type'], $allowed_types, true ) ) { 1982 _doing_it_wrong( 1983 __FUNCTION__, 1984 /* translators: 1: Parameter, 2: The list of allowed types. */ 1985 wp_sprintf( __( 'The "type" schema keyword for %1$s can only be one of the built-in types: %2$l.' ), $param, $allowed_types ), 1986 '5.5.0' 1987 ); 1988 } 1989 1990 if ( 'array' === $args['type'] ) { 1991 if ( ! rest_is_array( $value ) ) { 1992 return new WP_Error( 1993 'rest_invalid_type', 1994 /* translators: 1: Parameter, 2: Type name. */ 1995 sprintf( __( '%1$s is not of type %2$s.' ), $param, 'array' ), 1996 array( 'param' => $param ) 1997 ); 1998 } 1999 2000 $value = rest_sanitize_array( $value ); 2001 2002 if ( isset( $args['items'] ) ) { 2003 foreach ( $value as $index => $v ) { 2004 $is_valid = rest_validate_value_from_schema( $v, $args['items'], $param . '[' . $index . ']' ); 2005 if ( is_wp_error( $is_valid ) ) { 2006 return $is_valid; 2007 } 2008 } 2009 } 2010 2011 if ( isset( $args['minItems'] ) && count( $value ) < $args['minItems'] ) { 2012 /* translators: 1: Parameter, 2: Number. */ 2013 return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must contain at least %2$s items.' ), $param, number_format_i18n( $args['minItems'] ) ) ); 2014 } 2015 2016 if ( isset( $args['maxItems'] ) && count( $value ) > $args['maxItems'] ) { 2017 /* translators: 1: Parameter, 2: Number. */ 2018 return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must contain at most %2$s items.' ), $param, number_format_i18n( $args['maxItems'] ) ) ); 2019 } 2020 2021 if ( ! empty( $args['uniqueItems'] ) && ! rest_validate_array_contains_unique_items( $value ) ) { 2022 /* translators: 1: Parameter. */ 2023 return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s has duplicate items.' ), $param ) ); 2024 } 2025 } 2026 2027 if ( 'object' === $args['type'] ) { 2028 if ( ! rest_is_object( $value ) ) { 2029 return new WP_Error( 2030 'rest_invalid_type', 2031 /* translators: 1: Parameter, 2: Type name. */ 2032 sprintf( __( '%1$s is not of type %2$s.' ), $param, 'object' ), 2033 array( 'param' => $param ) 2034 ); 2035 } 2036 2037 $value = rest_sanitize_object( $value ); 2038 2039 if ( isset( $args['required'] ) && is_array( $args['required'] ) ) { // schema version 4 2040 foreach ( $args['required'] as $name ) { 2041 if ( ! array_key_exists( $name, $value ) ) { 2042 /* translators: 1: Property of an object, 2: Parameter. */ 2043 return new WP_Error( 'rest_property_required', sprintf( __( '%1$s is a required property of %2$s.' ), $name, $param ) ); 2044 } 2045 } 2046 } elseif ( isset( $args['properties'] ) ) { // schema version 3 2047 foreach ( $args['properties'] as $name => $property ) { 2048 if ( isset( $property['required'] ) && true === $property['required'] && ! array_key_exists( $name, $value ) ) { 2049 /* translators: 1: Property of an object, 2: Parameter. */ 2050 return new WP_Error( 'rest_property_required', sprintf( __( '%1$s is a required property of %2$s.' ), $name, $param ) ); 2051 } 2052 } 2053 } 2054 2055 foreach ( $value as $property => $v ) { 2056 if ( isset( $args['properties'][ $property ] ) ) { 2057 $is_valid = rest_validate_value_from_schema( $v, $args['properties'][ $property ], $param . '[' . $property . ']' ); 2058 if ( is_wp_error( $is_valid ) ) { 2059 return $is_valid; 2060 } 2061 continue; 2062 } 2063 2064 $pattern_property_schema = rest_find_matching_pattern_property_schema( $property, $args ); 2065 if ( null !== $pattern_property_schema ) { 2066 $is_valid = rest_validate_value_from_schema( $v, $pattern_property_schema, $param . '[' . $property . ']' ); 2067 if ( is_wp_error( $is_valid ) ) { 2068 return $is_valid; 2069 } 2070 continue; 2071 } 2072 2073 if ( isset( $args['additionalProperties'] ) ) { 2074 if ( false === $args['additionalProperties'] ) { 2075 /* translators: %s: Property of an object. */ 2076 return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s is not a valid property of Object.' ), $property ) ); 2077 } 2078 2079 if ( is_array( $args['additionalProperties'] ) ) { 2080 $is_valid = rest_validate_value_from_schema( $v, $args['additionalProperties'], $param . '[' . $property . ']' ); 2081 if ( is_wp_error( $is_valid ) ) { 2082 return $is_valid; 2083 } 2084 } 2085 } 2086 } 2087 2088 if ( isset( $args['minProperties'] ) && count( $value ) < $args['minProperties'] ) { 2089 /* translators: 1: Parameter, 2: Number. */ 2090 return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must contain at least %2$s properties.' ), $param, number_format_i18n( $args['minProperties'] ) ) ); 2091 } 2092 2093 if ( isset( $args['maxProperties'] ) && count( $value ) > $args['maxProperties'] ) { 2094 /* translators: 1: Parameter, 2: Number. */ 2095 return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must contain at most %2$s properties.' ), $param, number_format_i18n( $args['maxProperties'] ) ) ); 2096 } 2097 } 2098 2099 if ( 'null' === $args['type'] ) { 2100 if ( null !== $value ) { 2101 return new WP_Error( 2102 'rest_invalid_type', 2103 /* translators: 1: Parameter, 2: Type name. */ 2104 sprintf( __( '%1$s is not of type %2$s.' ), $param, 'null' ), 2105 array( 'param' => $param ) 2106 ); 2107 } 2108 2109 return true; 2110 } 2111 2112 if ( ! empty( $args['enum'] ) ) { 2113 if ( ! in_array( $value, $args['enum'], true ) ) { 2114 /* translators: 1: Parameter, 2: List of valid values. */ 2115 return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s is not one of %2$s.' ), $param, implode( ', ', $args['enum'] ) ) ); 2116 } 2117 } 2118 2119 if ( in_array( $args['type'], array( 'integer', 'number' ), true ) ) { 2120 if ( ! is_numeric( $value ) ) { 2121 return new WP_Error( 2122 'rest_invalid_type', 2123 /* translators: 1: Parameter, 2: Type name. */ 2124 sprintf( __( '%1$s is not of type %2$s.' ), $param, $args['type'] ), 2125 array( 'param' => $param ) 2126 ); 2127 } 2128 2129 if ( isset( $args['multipleOf'] ) && fmod( $value, $args['multipleOf'] ) !== 0.0 ) { 2130 /* translators: 1: Parameter, 2: Multiplier. */ 2131 return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be a multiple of %2$s.' ), $param, $args['multipleOf'] ) ); 2132 } 2133 } 2134 2135 if ( 'integer' === $args['type'] && ! rest_is_integer( $value ) ) { 2136 return new WP_Error( 2137 'rest_invalid_type', 2138 /* translators: 1: Parameter, 2: Type name. */ 2139 sprintf( __( '%1$s is not of type %2$s.' ), $param, 'integer' ), 2140 array( 'param' => $param ) 2141 ); 2142 } 2143 2144 if ( 'boolean' === $args['type'] && ! rest_is_boolean( $value ) ) { 2145 return new WP_Error( 2146 'rest_invalid_type', 2147 /* translators: 1: Parameter, 2: Type name. */ 2148 sprintf( __( '%1$s is not of type %2$s.' ), $param, 'boolean' ), 2149 array( 'param' => $param ) 2150 ); 2151 } 2152 2153 if ( 'string' === $args['type'] ) { 2154 if ( ! is_string( $value ) ) { 2155 return new WP_Error( 2156 'rest_invalid_type', 2157 /* translators: 1: Parameter, 2: Type name. */ 2158 sprintf( __( '%1$s is not of type %2$s.' ), $param, 'string' ), 2159 array( 'param' => $param ) 2160 ); 2161 } 2162 2163 if ( isset( $args['minLength'] ) && mb_strlen( $value ) < $args['minLength'] ) { 2164 return new WP_Error( 2165 'rest_invalid_param', 2166 sprintf( 2167 /* translators: 1: Parameter, 2: Number of characters. */ 2168 _n( '%1$s must be at least %2$s character long.', '%1$s must be at least %2$s characters long.', $args['minLength'] ), 2169 $param, 2170 number_format_i18n( $args['minLength'] ) 2171 ) 2172 ); 2173 } 2174 2175 if ( isset( $args['maxLength'] ) && mb_strlen( $value ) > $args['maxLength'] ) { 2176 return new WP_Error( 2177 'rest_invalid_param', 2178 sprintf( 2179 /* translators: 1: Parameter, 2: Number of characters. */ 2180 _n( '%1$s must be at most %2$s character long.', '%1$s must be at most %2$s characters long.', $args['maxLength'] ), 2181 $param, 2182 number_format_i18n( $args['maxLength'] ) 2183 ) 2184 ); 2185 } 2186 2187 if ( isset( $args['pattern'] ) && ! rest_validate_json_schema_pattern( $args['pattern'], $value ) ) { 2188 /* translators: 1: Parameter, 2: Pattern. */ 2189 return new WP_Error( 'rest_invalid_pattern', sprintf( __( '%1$s does not match pattern %2$s.' ), $param, $args['pattern'] ) ); 2190 } 2191 } 2192 2193 // The "format" keyword should only be applied to strings. However, for backward compatibility, 2194 // we allow the "format" keyword if the type keyword was not specified, or was set to an invalid value. 2195 if ( isset( $args['format'] ) 2196 && ( ! isset( $args['type'] ) || 'string' === $args['type'] || ! in_array( $args['type'], $allowed_types, true ) ) 2197 ) { 2198 switch ( $args['format'] ) { 2199 case 'hex-color': 2200 if ( ! rest_parse_hex_color( $value ) ) { 2201 return new WP_Error( 'rest_invalid_hex_color', __( 'Invalid hex color.' ) ); 2202 } 2203 break; 2204 2205 case 'date-time': 2206 if ( ! rest_parse_date( $value ) ) { 2207 return new WP_Error( 'rest_invalid_date', __( 'Invalid date.' ) ); 2208 } 2209 break; 2210 2211 case 'email': 2212 if ( ! is_email( $value ) ) { 2213 return new WP_Error( 'rest_invalid_email', __( 'Invalid email address.' ) ); 2214 } 2215 break; 2216 case 'ip': 2217 if ( ! rest_is_ip_address( $value ) ) { 2218 /* translators: %s: IP address. */ 2219 return new WP_Error( 'rest_invalid_param', sprintf( __( '%s is not a valid IP address.' ), $param ) ); 2220 } 2221 break; 2222 case 'uuid': 2223 if ( ! wp_is_uuid( $value ) ) { 2224 /* translators: %s: The name of a JSON field expecting a valid UUID. */ 2225 return new WP_Error( 'rest_invalid_uuid', sprintf( __( '%s is not a valid UUID.' ), $param ) ); 2226 } 2227 break; 2228 } 2229 } 2230 2231 if ( in_array( $args['type'], array( 'number', 'integer' ), true ) && ( isset( $args['minimum'] ) || isset( $args['maximum'] ) ) ) { 2232 if ( isset( $args['minimum'] ) && ! isset( $args['maximum'] ) ) { 2233 if ( ! empty( $args['exclusiveMinimum'] ) && $value <= $args['minimum'] ) { 2234 /* translators: 1: Parameter, 2: Minimum number. */ 2235 return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be greater than %2$d' ), $param, $args['minimum'] ) ); 2236 } elseif ( empty( $args['exclusiveMinimum'] ) && $value < $args['minimum'] ) { 2237 /* translators: 1: Parameter, 2: Minimum number. */ 2238 return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be greater than or equal to %2$d' ), $param, $args['minimum'] ) ); 2239 } 2240 } elseif ( isset( $args['maximum'] ) && ! isset( $args['minimum'] ) ) { 2241 if ( ! empty( $args['exclusiveMaximum'] ) && $value >= $args['maximum'] ) { 2242 /* translators: 1: Parameter, 2: Maximum number. */ 2243 return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be less than %2$d' ), $param, $args['maximum'] ) ); 2244 } elseif ( empty( $args['exclusiveMaximum'] ) && $value > $args['maximum'] ) { 2245 /* translators: 1: Parameter, 2: Maximum number. */ 2246 return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be less than or equal to %2$d' ), $param, $args['maximum'] ) ); 2247 } 2248 } elseif ( isset( $args['maximum'] ) && isset( $args['minimum'] ) ) { 2249 if ( ! empty( $args['exclusiveMinimum'] ) && ! empty( $args['exclusiveMaximum'] ) ) { 2250 if ( $value >= $args['maximum'] || $value <= $args['minimum'] ) { 2251 /* translators: 1: Parameter, 2: Minimum number, 3: Maximum number. */ 2252 return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be between %2$d (exclusive) and %3$d (exclusive)' ), $param, $args['minimum'], $args['maximum'] ) ); 2253 } 2254 } elseif ( empty( $args['exclusiveMinimum'] ) && ! empty( $args['exclusiveMaximum'] ) ) { 2255 if ( $value >= $args['maximum'] || $value < $args['minimum'] ) { 2256 /* translators: 1: Parameter, 2: Minimum number, 3: Maximum number. */ 2257 return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be between %2$d (inclusive) and %3$d (exclusive)' ), $param, $args['minimum'], $args['maximum'] ) ); 2258 } 2259 } elseif ( ! empty( $args['exclusiveMinimum'] ) && empty( $args['exclusiveMaximum'] ) ) { 2260 if ( $value > $args['maximum'] || $value <= $args['minimum'] ) { 2261 /* translators: 1: Parameter, 2: Minimum number, 3: Maximum number. */ 2262 return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be between %2$d (exclusive) and %3$d (inclusive)' ), $param, $args['minimum'], $args['maximum'] ) ); 2263 } 2264 } elseif ( empty( $args['exclusiveMinimum'] ) && empty( $args['exclusiveMaximum'] ) ) { 2265 if ( $value > $args['maximum'] || $value < $args['minimum'] ) { 2266 /* translators: 1: Parameter, 2: Minimum number, 3: Maximum number. */ 2267 return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s must be between %2$d (inclusive) and %3$d (inclusive)' ), $param, $args['minimum'], $args['maximum'] ) ); 2268 } 2269 } 2270 } 2271 } 2272 2273 return true; 2274 } 2275 2276 /** 2277 * Sanitize a value based on a schema. 2278 * 2279 * @since 4.7.0 2280 * @since 5.5.0 Added the `$param` parameter. 2281 * @since 5.6.0 Support the "anyOf" and "oneOf" keywords. 2282 * 2283 * @param mixed $value The value to sanitize. 2284 * @param array $args Schema array to use for sanitization. 2285 * @param string $param The parameter name, used in error messages. 2286 * @return mixed|WP_Error The sanitized value or a WP_Error instance if the value cannot be safely sanitized. 2287 */ 2288 function rest_sanitize_value_from_schema( $value, $args, $param = '' ) { 2289 if ( isset( $args['anyOf'] ) ) { 2290 $matching_schema = rest_find_any_matching_schema( $value, $args, $param ); 2291 if ( is_wp_error( $matching_schema ) ) { 2292 return $matching_schema; 2293 } 2294 2295 if ( ! isset( $args['type'] ) ) { 2296 $args['type'] = $matching_schema['type']; 2297 } 2298 2299 $value = rest_sanitize_value_from_schema( $value, $matching_schema, $param ); 2300 } 2301 2302 if ( isset( $args['oneOf'] ) ) { 2303 $matching_schema = rest_find_one_matching_schema( $value, $args, $param ); 2304 if ( is_wp_error( $matching_schema ) ) { 2305 return $matching_schema; 2306 } 2307 2308 if ( ! isset( $args['type'] ) ) { 2309 $args['type'] = $matching_schema['type']; 2310 } 2311 2312 $value = rest_sanitize_value_from_schema( $value, $matching_schema, $param ); 2313 } 2314 2315 $allowed_types = array( 'array', 'object', 'string', 'number', 'integer', 'boolean', 'null' ); 2316 2317 if ( ! isset( $args['type'] ) ) { 2318 /* translators: %s: Parameter. */ 2319 _doing_it_wrong( __FUNCTION__, sprintf( __( 'The "type" schema keyword for %s is required.' ), $param ), '5.5.0' ); 2320 } 2321 2322 if ( is_array( $args['type'] ) ) { 2323 $best_type = rest_handle_multi_type_schema( $value, $args, $param ); 2324 2325 if ( ! $best_type ) { 2326 return null; 2327 } 2328 2329 $args['type'] = $best_type; 2330 } 2331 2332 if ( ! in_array( $args['type'], $allowed_types, true ) ) { 2333 _doing_it_wrong( 2334 __FUNCTION__, 2335 /* translators: 1: Parameter, 2: The list of allowed types. */ 2336 wp_sprintf( __( 'The "type" schema keyword for %1$s can only be one of the built-in types: %2$l.' ), $param, $allowed_types ), 2337 '5.5.0' 2338 ); 2339 } 2340 2341 if ( 'array' === $args['type'] ) { 2342 $value = rest_sanitize_array( $value ); 2343 2344 if ( ! empty( $args['items'] ) ) { 2345 foreach ( $value as $index => $v ) { 2346 $value[ $index ] = rest_sanitize_value_from_schema( $v, $args['items'], $param . '[' . $index . ']' ); 2347 } 2348 } 2349 2350 if ( ! empty( $args['uniqueItems'] ) && ! rest_validate_array_contains_unique_items( $value ) ) { 2351 /* translators: 1: Parameter. */ 2352 return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s has duplicate items.' ), $param ) ); 2353 } 2354 2355 return $value; 2356 } 2357 2358 if ( 'object' === $args['type'] ) { 2359 $value = rest_sanitize_object( $value ); 2360 2361 foreach ( $value as $property => $v ) { 2362 if ( isset( $args['properties'][ $property ] ) ) { 2363 $value[ $property ] = rest_sanitize_value_from_schema( $v, $args['properties'][ $property ], $param . '[' . $property . ']' ); 2364 continue; 2365 } 2366 2367 $pattern_property_schema = rest_find_matching_pattern_property_schema( $property, $args ); 2368 if ( null !== $pattern_property_schema ) { 2369 $value[ $property ] = rest_sanitize_value_from_schema( $v, $pattern_property_schema, $param . '[' . $property . ']' ); 2370 continue; 2371 } 2372 2373 if ( isset( $args['additionalProperties'] ) ) { 2374 if ( false === $args['additionalProperties'] ) { 2375 unset( $value[ $property ] ); 2376 } elseif ( is_array( $args['additionalProperties'] ) ) { 2377 $value[ $property ] = rest_sanitize_value_from_schema( $v, $args['additionalProperties'], $param . '[' . $property . ']' ); 2378 } 2379 } 2380 } 2381 2382 return $value; 2383 } 2384 2385 if ( 'null' === $args['type'] ) { 2386 return null; 2387 } 2388 2389 if ( 'integer' === $args['type'] ) { 2390 return (int) $value; 2391 } 2392 2393 if ( 'number' === $args['type'] ) { 2394 return (float) $value; 2395 } 2396 2397 if ( 'boolean' === $args['type'] ) { 2398 return rest_sanitize_boolean( $value ); 2399 } 2400 2401 // This behavior matches rest_validate_value_from_schema(). 2402 if ( isset( $args['format'] ) 2403 && ( ! isset( $args['type'] ) || 'string' === $args['type'] || ! in_array( $args['type'], $allowed_types, true ) ) 2404 ) { 2405 switch ( $args['format'] ) { 2406 case 'hex-color': 2407 return (string) sanitize_hex_color( $value ); 2408 2409 case 'date-time': 2410 return sanitize_text_field( $value ); 2411 2412 case 'email': 2413 // sanitize_email() validates, which would be unexpected. 2414 return sanitize_text_field( $value ); 2415 2416 case 'uri': 2417 return esc_url_raw( $value ); 2418 2419 case 'ip': 2420 return sanitize_text_field( $value ); 2421 2422 case 'uuid': 2423 return sanitize_text_field( $value ); 2424 } 2425 } 2426 2427 if ( 'string' === $args['type'] ) { 2428 return (string) $value; 2429 } 2430 2431 return $value; 2432 } 2433 2434 /** 2435 * Append result of internal request to REST API for purpose of preloading data to be attached to a page. 2436 * Expected to be called in the context of `array_reduce`. 2437 * 2438 * @since 5.0.0 2439 * 2440 * @param array $memo Reduce accumulator. 2441 * @param string $path REST API path to preload. 2442 * @return array Modified reduce accumulator. 2443 */ 2444 function rest_preload_api_request( $memo, $path ) { 2445 // array_reduce() doesn't support passing an array in PHP 5.2, 2446 // so we need to make sure we start with one. 2447 if ( ! is_array( $memo ) ) { 2448 $memo = array(); 2449 } 2450 2451 if ( empty( $path ) ) { 2452 return $memo; 2453 } 2454 2455 $method = 'GET'; 2456 if ( is_array( $path ) && 2 === count( $path ) ) { 2457 $method = end( $path ); 2458 $path = reset( $path ); 2459 2460 if ( ! in_array( $method, array( 'GET', 'OPTIONS' ), true ) ) { 2461 $method = 'GET'; 2462 } 2463 } 2464 2465 $path_parts = parse_url( $path ); 2466 if ( false === $path_parts ) { 2467 return $memo; 2468 } 2469 2470 $request = new WP_REST_Request( $method, $path_parts['path'] ); 2471 if ( ! empty( $path_parts['query'] ) ) { 2472 parse_str( $path_parts['query'], $query_params ); 2473 $request->set_query_params( $query_params ); 2474 } 2475 2476 $response = rest_do_request( $request ); 2477 if ( 200 === $response->status ) { 2478 $server = rest_get_server(); 2479 $data = (array) $response->get_data(); 2480 $links = $server::get_compact_response_links( $response ); 2481 if ( ! empty( $links ) ) { 2482 $data['_links'] = $links; 2483 } 2484 2485 if ( 'OPTIONS' === $method ) { 2486 $response = rest_send_allow_header( $response, $server, $request ); 2487 2488 $memo[ $method ][ $path ] = array( 2489 'body' => $data, 2490 'headers' => $response->headers, 2491 ); 2492 } else { 2493 $memo[ $path ] = array( 2494 'body' => $data, 2495 'headers' => $response->headers, 2496 ); 2497 } 2498 } 2499 2500 return $memo; 2501 } 2502 2503 /** 2504 * Parses the "_embed" parameter into the list of resources to embed. 2505 * 2506 * @since 5.4.0 2507 * 2508 * @param string|array $embed Raw "_embed" parameter value. 2509 * @return true|string[] Either true to embed all embeds, or a list of relations to embed. 2510 */ 2511 function rest_parse_embed_param( $embed ) { 2512 if ( ! $embed || 'true' === $embed || '1' === $embed ) { 2513 return true; 2514 } 2515 2516 $rels = wp_parse_list( $embed ); 2517 2518 if ( ! $rels ) { 2519 return true; 2520 } 2521 2522 return $rels; 2523 } 2524 2525 /** 2526 * Filters the response to remove any fields not available in the given context. 2527 * 2528 * @since 5.5.0 2529 * @since 5.6.0 Support the "patternProperties" keyword for objects. 2530 * Support the "anyOf" and "oneOf" keywords. 2531 * 2532 * @param array|object $data The response data to modify. 2533 * @param array $schema The schema for the endpoint used to filter the response. 2534 * @param string $context The requested context. 2535 * @return array|object The filtered response data. 2536 */ 2537 function rest_filter_response_by_context( $data, $schema, $context ) { 2538 if ( isset( $schema['anyOf'] ) ) { 2539 $matching_schema = rest_find_any_matching_schema( $data, $schema, '' ); 2540 if ( ! is_wp_error( $matching_schema ) ) { 2541 if ( ! isset( $schema['type'] ) ) { 2542 $schema['type'] = $matching_schema['type']; 2543 } 2544 2545 $data = rest_filter_response_by_context( $data, $matching_schema, $context ); 2546 } 2547 } 2548 2549 if ( isset( $schema['oneOf'] ) ) { 2550 $matching_schema = rest_find_one_matching_schema( $data, $schema, '', true ); 2551 if ( ! is_wp_error( $matching_schema ) ) { 2552 if ( ! isset( $schema['type'] ) ) { 2553 $schema['type'] = $matching_schema['type']; 2554 } 2555 2556 $data = rest_filter_response_by_context( $data, $matching_schema, $context ); 2557 } 2558 } 2559 2560 if ( ! is_array( $data ) && ! is_object( $data ) ) { 2561 return $data; 2562 } 2563 2564 if ( isset( $schema['type'] ) ) { 2565 $type = $schema['type']; 2566 } elseif ( isset( $schema['properties'] ) ) { 2567 $type = 'object'; // Back compat if a developer accidentally omitted the type. 2568 } else { 2569 return $data; 2570 } 2571 2572 $is_array_type = 'array' === $type || ( is_array( $type ) && in_array( 'array', $type, true ) ); 2573 $is_object_type = 'object' === $type || ( is_array( $type ) && in_array( 'object', $type, true ) ); 2574 2575 if ( $is_array_type && $is_object_type ) { 2576 if ( rest_is_array( $data ) ) { 2577 $is_object_type = false; 2578 } else { 2579 $is_array_type = false; 2580 } 2581 } 2582 2583 $has_additional_properties = $is_object_type && isset( $schema['additionalProperties'] ) && is_array( $schema['additionalProperties'] ); 2584 2585 foreach ( $data as $key => $value ) { 2586 $check = array(); 2587 2588 if ( $is_array_type ) { 2589 $check = isset( $schema['items'] ) ? $schema['items'] : array(); 2590 } elseif ( $is_object_type ) { 2591 if ( isset( $schema['properties'][ $key ] ) ) { 2592 $check = $schema['properties'][ $key ]; 2593 } else { 2594 $pattern_property_schema = rest_find_matching_pattern_property_schema( $key, $schema ); 2595 if ( null !== $pattern_property_schema ) { 2596 $check = $pattern_property_schema; 2597 } elseif ( $has_additional_properties ) { 2598 $check = $schema['additionalProperties']; 2599 } 2600 } 2601 } 2602 2603 if ( ! isset( $check['context'] ) ) { 2604 continue; 2605 } 2606 2607 if ( ! in_array( $context, $check['context'], true ) ) { 2608 if ( $is_array_type ) { 2609 // All array items share schema, so there's no need to check each one. 2610 $data = array(); 2611 break; 2612 } 2613 2614 if ( is_object( $data ) ) { 2615 unset( $data->$key ); 2616 } else { 2617 unset( $data[ $key ] ); 2618 } 2619 } elseif ( is_array( $value ) || is_object( $value ) ) { 2620 $new_value = rest_filter_response_by_context( $value, $check, $context ); 2621 2622 if ( is_object( $data ) ) { 2623 $data->$key = $new_value; 2624 } else { 2625 $data[ $key ] = $new_value; 2626 } 2627 } 2628 } 2629 2630 return $data; 2631 } 2632 2633 /** 2634 * Sets the "additionalProperties" to false by default for all object definitions in the schema. 2635 * 2636 * @since 5.5.0 2637 * @since 5.6.0 Support the "patternProperties" keyword. 2638 * 2639 * @param array $schema The schema to modify. 2640 * @return array The modified schema. 2641 */ 2642 function rest_default_additional_properties_to_false( $schema ) { 2643 $type = (array) $schema['type']; 2644 2645 if ( in_array( 'object', $type, true ) ) { 2646 if ( isset( $schema['properties'] ) ) { 2647 foreach ( $schema['properties'] as $key => $child_schema ) { 2648 $schema['properties'][ $key ] = rest_default_additional_properties_to_false( $child_schema ); 2649 } 2650 } 2651 2652 if ( isset( $schema['patternProperties'] ) ) { 2653 foreach ( $schema['patternProperties'] as $key => $child_schema ) { 2654 $schema['patternProperties'][ $key ] = rest_default_additional_properties_to_false( $child_schema ); 2655 } 2656 } 2657 2658 if ( ! isset( $schema['additionalProperties'] ) ) { 2659 $schema['additionalProperties'] = false; 2660 } 2661 } 2662 2663 if ( in_array( 'array', $type, true ) ) { 2664 if ( isset( $schema['items'] ) ) { 2665 $schema['items'] = rest_default_additional_properties_to_false( $schema['items'] ); 2666 } 2667 } 2668 2669 return $schema; 2670 } 2671 2672 /** 2673 * Gets the REST API route for a post. 2674 * 2675 * @since 5.5.0 2676 * 2677 * @param int|WP_Post $post Post ID or post object. 2678 * @return string The route path with a leading slash for the given post, or an empty string if there is not a route. 2679 */ 2680 function rest_get_route_for_post( $post ) { 2681 $post = get_post( $post ); 2682 2683 if ( ! $post instanceof WP_Post ) { 2684 return ''; 2685 } 2686 2687 $post_type = get_post_type_object( $post->post_type ); 2688 if ( ! $post_type ) { 2689 return ''; 2690 } 2691 2692 $controller = $post_type->get_rest_controller(); 2693 if ( ! $controller ) { 2694 return ''; 2695 } 2696 2697 $route = ''; 2698 2699 // The only two controllers that we can detect are the Attachments and Posts controllers. 2700 if ( in_array( get_class( $controller ), array( 'WP_REST_Attachments_Controller', 'WP_REST_Posts_Controller' ), true ) ) { 2701 $namespace = 'wp/v2'; 2702 $rest_base = ! empty( $post_type->rest_base ) ? $post_type->rest_base : $post_type->name; 2703 $route = sprintf( '/%s/%s/%d', $namespace, $rest_base, $post->ID ); 2704 } 2705 2706 /** 2707 * Filters the REST API route for a post. 2708 * 2709 * @since 5.5.0 2710 * 2711 * @param string $route The route path. 2712 * @param WP_Post $post The post object. 2713 */ 2714 return apply_filters( 'rest_route_for_post', $route, $post ); 2715 } 2716 2717 /** 2718 * Gets the REST API route for a term. 2719 * 2720 * @since 5.5.0 2721 * 2722 * @param int|WP_Term $term Term ID or term object. 2723 * @return string The route path with a leading slash for the given term, or an empty string if there is not a route. 2724 */ 2725 function rest_get_route_for_term( $term ) { 2726 $term = get_term( $term ); 2727 2728 if ( ! $term instanceof WP_Term ) { 2729 return ''; 2730 } 2731 2732 $taxonomy = get_taxonomy( $term->taxonomy ); 2733 if ( ! $taxonomy ) { 2734 return ''; 2735 } 2736 2737 $controller = $taxonomy->get_rest_controller(); 2738 if ( ! $controller ) { 2739 return ''; 2740 } 2741 2742 $route = ''; 2743 2744 // The only controller that works is the Terms controller. 2745 if ( $controller instanceof WP_REST_Terms_Controller ) { 2746 $namespace = 'wp/v2'; 2747 $rest_base = ! empty( $taxonomy->rest_base ) ? $taxonomy->rest_base : $taxonomy->name; 2748 $route = sprintf( '/%s/%s/%d', $namespace, $rest_base, $term->term_id ); 2749 } 2750 2751 /** 2752 * Filters the REST API route for a term. 2753 * 2754 * @since 5.5.0 2755 * 2756 * @param string $route The route path. 2757 * @param WP_Term $term The term object. 2758 */ 2759 return apply_filters( 'rest_route_for_term', $route, $term ); 2760 } 2761 2762 /** 2763 * Gets the REST route for the currently queried object. 2764 * 2765 * @since 5.5.0 2766 * 2767 * @return string The REST route of the resource, or an empty string if no resource identified. 2768 */ 2769 function rest_get_queried_resource_route() { 2770 if ( is_singular() ) { 2771 $route = rest_get_route_for_post( get_queried_object() ); 2772 } elseif ( is_category() || is_tag() || is_tax() ) { 2773 $route = rest_get_route_for_term( get_queried_object() ); 2774 } elseif ( is_author() ) { 2775 $route = '/wp/v2/users/' . get_queried_object_id(); 2776 } else { 2777 $route = ''; 2778 } 2779 2780 /** 2781 * Filters the REST route for the currently queried object. 2782 * 2783 * @since 5.5.0 2784 * 2785 * @param string $link The route with a leading slash, or an empty string. 2786 */ 2787 return apply_filters( 'rest_queried_resource_route', $route ); 2788 } 2789 2790 /** 2791 * Retrieves an array of endpoint arguments from the item schema and endpoint method. 2792 * 2793 * @since 5.6.0 2794 * 2795 * @param array $schema The full JSON schema for the endpoint. 2796 * @param string $method Optional. HTTP method of the endpoint. The arguments for `CREATABLE` endpoints are 2797 * checked for required values and may fall-back to a given default, this is not done 2798 * on `EDITABLE` endpoints. Default WP_REST_Server::CREATABLE. 2799 * @return array The endpoint arguments. 2800 */ 2801 function rest_get_endpoint_args_for_schema( $schema, $method = WP_REST_Server::CREATABLE ) { 2802 2803 $schema_properties = ! empty( $schema['properties'] ) ? $schema['properties'] : array(); 2804 $endpoint_args = array(); 2805 $valid_schema_properties = rest_get_allowed_schema_keywords(); 2806 $valid_schema_properties = array_diff( $valid_schema_properties, array( 'default', 'required' ) ); 2792 2807 2793 2808 foreach ( $schema_properties as $field_id => $params ) { … … 2802 2817 'sanitize_callback' => 'rest_sanitize_request_arg', 2803 2818 ); 2804 2805 if ( isset( $params['description'] ) ) {2806 $endpoint_args[ $field_id ]['description'] = $params['description'];2807 }2808 2819 2809 2820 if ( WP_REST_Server::CREATABLE === $method && isset( $params['default'] ) ) {
Note: See TracChangeset
for help on using the changeset viewer.