Changeset 48306
- Timestamp:
- 07/05/2020 12:13:37 AM (4 years ago)
- Location:
- trunk
- Files:
-
- 4 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/wp-includes/rest-api.php
r48302 r48306 987 987 988 988 /** 989 * Parses an RFC3339 time into a Unix timestamp.990 *991 * @since 4.4.0992 *993 * @param string $date RFC3339 timestamp.994 * @param bool $force_utc Optional. Whether to force UTC timezone instead of using995 * the timestamp's timezone. Default false.996 * @return int Unix timestamp.997 */998 function rest_parse_date( $date, $force_utc = false ) {999 if ( $force_utc ) {1000 $date = preg_replace( '/[+-]\d+:?\d+$/', '+00:00', $date );1001 }1002 1003 $regex = '#^\d{4}-\d{2}-\d{2}[Tt ]\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}(?::\d{2})?)?$#';1004 1005 if ( ! preg_match( $regex, $date, $matches ) ) {1006 return false;1007 }1008 1009 return strtotime( $date );1010 }1011 1012 /**1013 * Parses a 3 or 6 digit hex color (with #).1014 *1015 * @since 5.4.01016 *1017 * @param string $color 3 or 6 digit hex color (with #).1018 * @return string|false1019 */1020 function rest_parse_hex_color( $color ) {1021 $regex = '|^#([A-Fa-f0-9]{3}){1,2}$|';1022 if ( ! preg_match( $regex, $color, $matches ) ) {1023 return false;1024 }1025 1026 return $color;1027 }1028 1029 /**1030 * Parses a date into both its local and UTC equivalent, in MySQL datetime format.1031 *1032 * @since 4.4.01033 *1034 * @see rest_parse_date()1035 *1036 * @param string $date RFC3339 timestamp.1037 * @param bool $is_utc Whether the provided date should be interpreted as UTC. Default false.1038 * @return array|null Local and UTC datetime strings, in MySQL datetime format (Y-m-d H:i:s),1039 * null on failure.1040 */1041 function rest_get_date_with_gmt( $date, $is_utc = false ) {1042 /*1043 * Whether or not the original date actually has a timezone string1044 * changes the way we need to do timezone conversion.1045 * Store this info before parsing the date, and use it later.1046 */1047 $has_timezone = preg_match( '#(Z|[+-]\d{2}(:\d{2})?)$#', $date );1048 1049 $date = rest_parse_date( $date );1050 1051 if ( empty( $date ) ) {1052 return null;1053 }1054 1055 /*1056 * At this point $date could either be a local date (if we were passed1057 * a *local* date without a timezone offset) or a UTC date (otherwise).1058 * Timezone conversion needs to be handled differently between these two cases.1059 */1060 if ( ! $is_utc && ! $has_timezone ) {1061 $local = gmdate( 'Y-m-d H:i:s', $date );1062 $utc = get_gmt_from_date( $local );1063 } else {1064 $utc = gmdate( 'Y-m-d H:i:s', $date );1065 $local = get_date_from_gmt( $utc );1066 }1067 1068 return array( $local, $utc );1069 }1070 1071 /**1072 * Returns a contextual HTTP error code for authorization failure.1073 *1074 * @since 4.7.01075 *1076 * @return integer 401 if the user is not logged in, 403 if the user is logged in.1077 */1078 function rest_authorization_required_code() {1079 return is_user_logged_in() ? 403 : 401;1080 }1081 1082 /**1083 * Validate a request argument based on details registered to the route.1084 *1085 * @since 4.7.01086 *1087 * @param mixed $value1088 * @param WP_REST_Request $request1089 * @param string $param1090 * @return true|WP_Error1091 */1092 function rest_validate_request_arg( $value, $request, $param ) {1093 $attributes = $request->get_attributes();1094 if ( ! isset( $attributes['args'][ $param ] ) || ! is_array( $attributes['args'][ $param ] ) ) {1095 return true;1096 }1097 $args = $attributes['args'][ $param ];1098 1099 return rest_validate_value_from_schema( $value, $args, $param );1100 }1101 1102 /**1103 * Sanitize a request argument based on details registered to the route.1104 *1105 * @since 4.7.01106 *1107 * @param mixed $value1108 * @param WP_REST_Request $request1109 * @param string $param1110 * @return mixed1111 */1112 function rest_sanitize_request_arg( $value, $request, $param ) {1113 $attributes = $request->get_attributes();1114 if ( ! isset( $attributes['args'][ $param ] ) || ! is_array( $attributes['args'][ $param ] ) ) {1115 return $value;1116 }1117 $args = $attributes['args'][ $param ];1118 1119 return rest_sanitize_value_from_schema( $value, $args );1120 }1121 1122 /**1123 * Parse a request argument based on details registered to the route.1124 *1125 * Runs a validation check and sanitizes the value, primarily to be used via1126 * the `sanitize_callback` arguments in the endpoint args registration.1127 *1128 * @since 4.7.01129 *1130 * @param mixed $value1131 * @param WP_REST_Request $request1132 * @param string $param1133 * @return mixed1134 */1135 function rest_parse_request_arg( $value, $request, $param ) {1136 $is_valid = rest_validate_request_arg( $value, $request, $param );1137 1138 if ( is_wp_error( $is_valid ) ) {1139 return $is_valid;1140 }1141 1142 $value = rest_sanitize_request_arg( $value, $request, $param );1143 1144 return $value;1145 }1146 1147 /**1148 * Determines if an IP address is valid.1149 *1150 * Handles both IPv4 and IPv6 addresses.1151 *1152 * @since 4.7.01153 *1154 * @param string $ip IP address.1155 * @return string|false The valid IP address, otherwise false.1156 */1157 function rest_is_ip_address( $ip ) {1158 $ipv4_pattern = '/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/';1159 1160 if ( ! preg_match( $ipv4_pattern, $ip ) && ! Requests_IPv6::check_ipv6( $ip ) ) {1161 return false;1162 }1163 1164 return $ip;1165 }1166 1167 /**1168 * Changes a boolean-like value into the proper boolean value.1169 *1170 * @since 4.7.01171 *1172 * @param bool|string|int $value The value being evaluated.1173 * @return boolean Returns the proper associated boolean value.1174 */1175 function rest_sanitize_boolean( $value ) {1176 // String values are translated to `true`; make sure 'false' is false.1177 if ( is_string( $value ) ) {1178 $value = strtolower( $value );1179 if ( in_array( $value, array( 'false', '0' ), true ) ) {1180 $value = false;1181 }1182 }1183 1184 // Everything else will map nicely to boolean.1185 return (bool) $value;1186 }1187 1188 /**1189 * Determines if a given value is boolean-like.1190 *1191 * @since 4.7.01192 *1193 * @param bool|string $maybe_bool The value being evaluated.1194 * @return boolean True if a boolean, otherwise false.1195 */1196 function rest_is_boolean( $maybe_bool ) {1197 if ( is_bool( $maybe_bool ) ) {1198 return true;1199 }1200 1201 if ( is_string( $maybe_bool ) ) {1202 $maybe_bool = strtolower( $maybe_bool );1203 1204 $valid_boolean_values = array(1205 'false',1206 'true',1207 '0',1208 '1',1209 );1210 1211 return in_array( $maybe_bool, $valid_boolean_values, true );1212 }1213 1214 if ( is_int( $maybe_bool ) ) {1215 return in_array( $maybe_bool, array( 0, 1 ), true );1216 }1217 1218 return false;1219 }1220 1221 /**1222 989 * Retrieves the avatar urls in various sizes. 1223 990 * … … 1264 1031 1265 1032 /** 1033 * Parses an RFC3339 time into a Unix timestamp. 1034 * 1035 * @since 4.4.0 1036 * 1037 * @param string $date RFC3339 timestamp. 1038 * @param bool $force_utc Optional. Whether to force UTC timezone instead of using 1039 * the timestamp's timezone. Default false. 1040 * @return int Unix timestamp. 1041 */ 1042 function rest_parse_date( $date, $force_utc = false ) { 1043 if ( $force_utc ) { 1044 $date = preg_replace( '/[+-]\d+:?\d+$/', '+00:00', $date ); 1045 } 1046 1047 $regex = '#^\d{4}-\d{2}-\d{2}[Tt ]\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}(?::\d{2})?)?$#'; 1048 1049 if ( ! preg_match( $regex, $date, $matches ) ) { 1050 return false; 1051 } 1052 1053 return strtotime( $date ); 1054 } 1055 1056 /** 1057 * Parses a 3 or 6 digit hex color (with #). 1058 * 1059 * @since 5.4.0 1060 * 1061 * @param string $color 3 or 6 digit hex color (with #). 1062 * @return string|false 1063 */ 1064 function rest_parse_hex_color( $color ) { 1065 $regex = '|^#([A-Fa-f0-9]{3}){1,2}$|'; 1066 if ( ! preg_match( $regex, $color, $matches ) ) { 1067 return false; 1068 } 1069 1070 return $color; 1071 } 1072 1073 /** 1074 * Parses a date into both its local and UTC equivalent, in MySQL datetime format. 1075 * 1076 * @since 4.4.0 1077 * 1078 * @see rest_parse_date() 1079 * 1080 * @param string $date RFC3339 timestamp. 1081 * @param bool $is_utc Whether the provided date should be interpreted as UTC. Default false. 1082 * @return array|null Local and UTC datetime strings, in MySQL datetime format (Y-m-d H:i:s), 1083 * null on failure. 1084 */ 1085 function rest_get_date_with_gmt( $date, $is_utc = false ) { 1086 /* 1087 * Whether or not the original date actually has a timezone string 1088 * changes the way we need to do timezone conversion. 1089 * Store this info before parsing the date, and use it later. 1090 */ 1091 $has_timezone = preg_match( '#(Z|[+-]\d{2}(:\d{2})?)$#', $date ); 1092 1093 $date = rest_parse_date( $date ); 1094 1095 if ( empty( $date ) ) { 1096 return null; 1097 } 1098 1099 /* 1100 * At this point $date could either be a local date (if we were passed 1101 * a *local* date without a timezone offset) or a UTC date (otherwise). 1102 * Timezone conversion needs to be handled differently between these two cases. 1103 */ 1104 if ( ! $is_utc && ! $has_timezone ) { 1105 $local = gmdate( 'Y-m-d H:i:s', $date ); 1106 $utc = get_gmt_from_date( $local ); 1107 } else { 1108 $utc = gmdate( 'Y-m-d H:i:s', $date ); 1109 $local = get_date_from_gmt( $utc ); 1110 } 1111 1112 return array( $local, $utc ); 1113 } 1114 1115 /** 1116 * Returns a contextual HTTP error code for authorization failure. 1117 * 1118 * @since 4.7.0 1119 * 1120 * @return integer 401 if the user is not logged in, 403 if the user is logged in. 1121 */ 1122 function rest_authorization_required_code() { 1123 return is_user_logged_in() ? 403 : 401; 1124 } 1125 1126 /** 1127 * Validate a request argument based on details registered to the route. 1128 * 1129 * @since 4.7.0 1130 * 1131 * @param mixed $value 1132 * @param WP_REST_Request $request 1133 * @param string $param 1134 * @return true|WP_Error 1135 */ 1136 function rest_validate_request_arg( $value, $request, $param ) { 1137 $attributes = $request->get_attributes(); 1138 if ( ! isset( $attributes['args'][ $param ] ) || ! is_array( $attributes['args'][ $param ] ) ) { 1139 return true; 1140 } 1141 $args = $attributes['args'][ $param ]; 1142 1143 return rest_validate_value_from_schema( $value, $args, $param ); 1144 } 1145 1146 /** 1147 * Sanitize a request argument based on details registered to the route. 1148 * 1149 * @since 4.7.0 1150 * 1151 * @param mixed $value 1152 * @param WP_REST_Request $request 1153 * @param string $param 1154 * @return mixed 1155 */ 1156 function rest_sanitize_request_arg( $value, $request, $param ) { 1157 $attributes = $request->get_attributes(); 1158 if ( ! isset( $attributes['args'][ $param ] ) || ! is_array( $attributes['args'][ $param ] ) ) { 1159 return $value; 1160 } 1161 $args = $attributes['args'][ $param ]; 1162 1163 return rest_sanitize_value_from_schema( $value, $args, $param ); 1164 } 1165 1166 /** 1167 * Parse a request argument based on details registered to the route. 1168 * 1169 * Runs a validation check and sanitizes the value, primarily to be used via 1170 * the `sanitize_callback` arguments in the endpoint args registration. 1171 * 1172 * @since 4.7.0 1173 * 1174 * @param mixed $value 1175 * @param WP_REST_Request $request 1176 * @param string $param 1177 * @return mixed 1178 */ 1179 function rest_parse_request_arg( $value, $request, $param ) { 1180 $is_valid = rest_validate_request_arg( $value, $request, $param ); 1181 1182 if ( is_wp_error( $is_valid ) ) { 1183 return $is_valid; 1184 } 1185 1186 $value = rest_sanitize_request_arg( $value, $request, $param ); 1187 1188 return $value; 1189 } 1190 1191 /** 1192 * Determines if an IP address is valid. 1193 * 1194 * Handles both IPv4 and IPv6 addresses. 1195 * 1196 * @since 4.7.0 1197 * 1198 * @param string $ip IP address. 1199 * @return string|false The valid IP address, otherwise false. 1200 */ 1201 function rest_is_ip_address( $ip ) { 1202 $ipv4_pattern = '/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/'; 1203 1204 if ( ! preg_match( $ipv4_pattern, $ip ) && ! Requests_IPv6::check_ipv6( $ip ) ) { 1205 return false; 1206 } 1207 1208 return $ip; 1209 } 1210 1211 /** 1212 * Changes a boolean-like value into the proper boolean value. 1213 * 1214 * @since 4.7.0 1215 * 1216 * @param bool|string|int $value The value being evaluated. 1217 * @return boolean Returns the proper associated boolean value. 1218 */ 1219 function rest_sanitize_boolean( $value ) { 1220 // String values are translated to `true`; make sure 'false' is false. 1221 if ( is_string( $value ) ) { 1222 $value = strtolower( $value ); 1223 if ( in_array( $value, array( 'false', '0' ), true ) ) { 1224 $value = false; 1225 } 1226 } 1227 1228 // Everything else will map nicely to boolean. 1229 return (bool) $value; 1230 } 1231 1232 /** 1233 * Determines if a given value is boolean-like. 1234 * 1235 * @since 4.7.0 1236 * 1237 * @param bool|string $maybe_bool The value being evaluated. 1238 * @return boolean True if a boolean, otherwise false. 1239 */ 1240 function rest_is_boolean( $maybe_bool ) { 1241 if ( is_bool( $maybe_bool ) ) { 1242 return true; 1243 } 1244 1245 if ( is_string( $maybe_bool ) ) { 1246 $maybe_bool = strtolower( $maybe_bool ); 1247 1248 $valid_boolean_values = array( 1249 'false', 1250 'true', 1251 '0', 1252 '1', 1253 ); 1254 1255 return in_array( $maybe_bool, $valid_boolean_values, true ); 1256 } 1257 1258 if ( is_int( $maybe_bool ) ) { 1259 return in_array( $maybe_bool, array( 0, 1 ), true ); 1260 } 1261 1262 return false; 1263 } 1264 1265 /** 1266 * Determines if a given value is integer-like. 1267 * 1268 * @since 5.5.0 1269 * 1270 * @param mixed $maybe_integer The value being evaluated. 1271 * @return bool True if an integer, otherwise false. 1272 */ 1273 function rest_is_integer( $maybe_integer ) { 1274 return round( floatval( $maybe_integer ) ) === floatval( $maybe_integer ); 1275 } 1276 1277 /** 1278 * Determines if a given value is array-like. 1279 * 1280 * @since 5.5.0 1281 * 1282 * @param mixed $maybe_array The value being evaluated. 1283 * @return bool 1284 */ 1285 function rest_is_array( $maybe_array ) { 1286 if ( is_scalar( $maybe_array ) ) { 1287 $maybe_array = wp_parse_list( $maybe_array ); 1288 } 1289 1290 return wp_is_numeric_array( $maybe_array ); 1291 } 1292 1293 /** 1294 * Converts an array-like value to an array. 1295 * 1296 * @since 5.5.0 1297 * 1298 * @param mixed $maybe_array The value being evaluated. 1299 * @return array Returns the array extracted from the value. 1300 */ 1301 function rest_sanitize_array( $maybe_array ) { 1302 if ( is_scalar( $maybe_array ) ) { 1303 return wp_parse_list( $maybe_array ); 1304 } 1305 1306 if ( ! is_array( $maybe_array ) ) { 1307 return array(); 1308 } 1309 1310 // Normalize to numeric array so nothing unexpected is in the keys. 1311 return array_values( $maybe_array ); 1312 } 1313 1314 /** 1315 * Determines if a given value is object-like. 1316 * 1317 * @since 5.5.0 1318 * 1319 * @param mixed $maybe_object The value being evaluated. 1320 * @return bool True if object like, otherwise false. 1321 */ 1322 function rest_is_object( $maybe_object ) { 1323 if ( '' === $maybe_object ) { 1324 return true; 1325 } 1326 1327 if ( $maybe_object instanceof stdClass ) { 1328 return true; 1329 } 1330 1331 if ( $maybe_object instanceof JsonSerializable ) { 1332 $maybe_object = $maybe_object->jsonSerialize(); 1333 } 1334 1335 return is_array( $maybe_object ); 1336 } 1337 1338 /** 1339 * Converts an object-like value to an object. 1340 * 1341 * @since 5.5.0 1342 * 1343 * @param mixed $maybe_object The value being evaluated. 1344 * @return array Returns the object extracted from the value. 1345 */ 1346 function rest_sanitize_object( $maybe_object ) { 1347 if ( '' === $maybe_object ) { 1348 return array(); 1349 } 1350 1351 if ( $maybe_object instanceof stdClass ) { 1352 return (array) $maybe_object; 1353 } 1354 1355 if ( $maybe_object instanceof JsonSerializable ) { 1356 $maybe_object = $maybe_object->jsonSerialize(); 1357 } 1358 1359 if ( ! is_array( $maybe_object ) ) { 1360 return array(); 1361 } 1362 1363 return $maybe_object; 1364 } 1365 1366 /** 1367 * Gets the best type for a value. 1368 * 1369 * @since 5.5.0 1370 * 1371 * @param mixed $value The value to check. 1372 * @param array $types The list of possible types. 1373 * @return string The best matching type, an empty string if no types match. 1374 */ 1375 function rest_get_best_type_for_value( $value, $types ) { 1376 static $checks = array( 1377 'array' => 'rest_is_array', 1378 'object' => 'rest_is_object', 1379 'integer' => 'rest_is_integer', 1380 'number' => 'is_numeric', 1381 'boolean' => 'rest_is_boolean', 1382 'string' => 'is_string', 1383 'null' => 'is_null', 1384 ); 1385 1386 // Both arrays and objects allow empty strings to be converted to their types. 1387 // But the best answer for this type is a string. 1388 if ( '' === $value && in_array( 'string', $types, true ) ) { 1389 return 'string'; 1390 } 1391 1392 foreach ( $types as $type ) { 1393 if ( isset( $checks[ $type ] ) && $checks[ $type ]( $value ) ) { 1394 return $type; 1395 } 1396 } 1397 1398 return ''; 1399 } 1400 1401 /** 1402 * Handles getting the best type for a multi-type schema. 1403 * 1404 * This is a wrapper for {@see rest_get_best_type_for_value()} that handles 1405 * backward compatibility for schemas that use invalid types. 1406 * 1407 * @since 5.5.0 1408 * 1409 * @param mixed $value The value to check. 1410 * @param array $args The schema array to use. 1411 * @param string $param The parameter name, used in error messages. 1412 * @return string 1413 */ 1414 function rest_handle_multi_type_schema( $value, $args, $param = '' ) { 1415 $allowed_types = array( 'array', 'object', 'string', 'number', 'integer', 'boolean', 'null' ); 1416 $invalid_types = array_diff( $args['type'], $allowed_types ); 1417 1418 if ( $invalid_types ) { 1419 _doing_it_wrong( 1420 __FUNCTION__, 1421 /* translators: 1. Parameter. 2. List of allowed types. */ 1422 wp_sprintf( __( 'The "type" schema keyword for %1$s can only contain the built-in types: %2$l.' ), $param, $allowed_types ), 1423 '5.5.0' 1424 ); 1425 } 1426 1427 $best_type = rest_get_best_type_for_value( $value, $args['type'] ); 1428 1429 if ( ! $best_type ) { 1430 if ( ! $invalid_types ) { 1431 return ''; 1432 } 1433 1434 // Backward compatibility for previous behavior which allowed the value if there was an invalid type used. 1435 $best_type = reset( $invalid_types ); 1436 } 1437 1438 return $best_type; 1439 } 1440 1441 /** 1266 1442 * Validate a value based on a schema. 1267 1443 * … … 1285 1461 1286 1462 if ( ! isset( $args['type'] ) ) { 1287 _doing_it_wrong( __FUNCTION__, __( 'The "type" schema keyword is required.' ), '5.5.0' ); 1463 /* translators: 1. Parameter */ 1464 _doing_it_wrong( __FUNCTION__, sprintf( __( 'The "type" schema keyword for %s is required.' ), $param ), '5.5.0' ); 1288 1465 } 1289 1466 1290 1467 if ( is_array( $args['type'] ) ) { 1291 foreach ( $args['type'] as $type ) { 1292 $type_args = $args; 1293 $type_args['type'] = $type; 1294 1295 if ( true === rest_validate_value_from_schema( $value, $type_args, $param ) ) { 1296 return true; 1297 } 1298 } 1299 1300 /* translators: 1: Parameter, 2: List of types. */ 1301 return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s is not of type %2$s.' ), $param, implode( ',', $args['type'] ) ) ); 1468 $best_type = rest_handle_multi_type_schema( $value, $args, $param ); 1469 1470 if ( ! $best_type ) { 1471 /* translators: 1: Parameter, 2: List of types. */ 1472 return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s is not of type %2$s.' ), $param, implode( ',', $args['type'] ) ) ); 1473 } 1474 1475 $args['type'] = $best_type; 1302 1476 } 1303 1477 … … 1305 1479 _doing_it_wrong( 1306 1480 __FUNCTION__, 1307 /* translators: 1. The list of allowed types. */1308 wp_sprintf( __( 'The "type" schema keyword can only be on of the built-in types: %l.' ), $allowed_types ),1481 /* translators: 1. Parameter 2. The list of allowed types. */ 1482 wp_sprintf( __( 'The "type" schema keyword for %1$s can only be on of the built-in types: %2$l.' ), $param, $allowed_types ), 1309 1483 '5.5.0' 1310 1484 ); … … 1312 1486 1313 1487 if ( 'array' === $args['type'] ) { 1314 if ( ! is_null( $value ) ) { 1315 $value = wp_parse_list( $value ); 1316 } 1317 1318 if ( ! wp_is_numeric_array( $value ) ) { 1488 if ( ! rest_is_array( $value ) ) { 1319 1489 /* translators: 1: Parameter, 2: Type name. */ 1320 1490 return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s is not of type %2$s.' ), $param, 'array' ) ); 1321 1491 } 1492 1493 $value = rest_sanitize_array( $value ); 1322 1494 1323 1495 foreach ( $value as $index => $v ) { … … 1340 1512 1341 1513 if ( 'object' === $args['type'] ) { 1342 if ( '' === $value ) { 1343 $value = array(); 1344 } 1345 1346 if ( $value instanceof stdClass ) { 1347 $value = (array) $value; 1348 } 1349 1350 if ( $value instanceof JsonSerializable ) { 1351 $value = $value->jsonSerialize(); 1352 } 1353 1354 if ( ! is_array( $value ) ) { 1514 if ( ! rest_is_object( $value ) ) { 1355 1515 /* translators: 1: Parameter, 2: Type name. */ 1356 1516 return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s is not of type %2$s.' ), $param, 'object' ) ); 1357 1517 } 1518 1519 $value = rest_sanitize_object( $value ); 1358 1520 1359 1521 if ( isset( $args['required'] ) && is_array( $args['required'] ) ) { // schema version 4 … … 1416 1578 } 1417 1579 1418 if ( 'integer' === $args['type'] && round( floatval( $value ) ) !== floatval( $value ) ) {1580 if ( 'integer' === $args['type'] && ! rest_is_integer( $value ) ) { 1419 1581 /* translators: 1: Parameter, 2: Type name. */ 1420 1582 return new WP_Error( 'rest_invalid_param', sprintf( __( '%1$s is not of type %2$s.' ), $param, 'integer' ) ); … … 1552 1714 * 1553 1715 * @since 4.7.0 1554 * 1555 * @param mixed $value The value to sanitize. 1556 * @param array $args Schema array to use for sanitization. 1716 * @since 5.5.0 Added the `$param` parameter. 1717 * 1718 * @param mixed $value The value to sanitize. 1719 * @param array $args Schema array to use for sanitization. 1720 * @param string $param The parameter name, used in error messages. 1557 1721 * @return true|WP_Error 1558 1722 */ 1559 function rest_sanitize_value_from_schema( $value, $args ) {1723 function rest_sanitize_value_from_schema( $value, $args, $param = '' ) { 1560 1724 $allowed_types = array( 'array', 'object', 'string', 'number', 'integer', 'boolean', 'null' ); 1561 1725 1562 1726 if ( ! isset( $args['type'] ) ) { 1563 _doing_it_wrong( __FUNCTION__, __( 'The "type" schema keyword is required.' ), '5.5.0' ); 1727 /* translators: 1. Parameter */ 1728 _doing_it_wrong( __FUNCTION__, sprintf( __( 'The "type" schema keyword for %s is required.' ), $param ), '5.5.0' ); 1564 1729 } 1565 1730 1566 1731 if ( is_array( $args['type'] ) ) { 1567 // Determine which type the value was validated against, 1568 // and use that type when performing sanitization. 1569 $validated_type = ''; 1570 1571 foreach ( $args['type'] as $type ) { 1572 $type_args = $args; 1573 $type_args['type'] = $type; 1574 1575 if ( ! is_wp_error( rest_validate_value_from_schema( $value, $type_args ) ) ) { 1576 $validated_type = $type; 1577 break; 1578 } 1579 } 1580 1581 if ( ! $validated_type ) { 1732 $best_type = rest_handle_multi_type_schema( $value, $args, $param ); 1733 1734 if ( ! $best_type ) { 1582 1735 return null; 1583 1736 } 1584 1737 1585 $args['type'] = $ validated_type;1738 $args['type'] = $best_type; 1586 1739 } 1587 1740 … … 1589 1742 _doing_it_wrong( 1590 1743 __FUNCTION__, 1591 /* translators: 1. The list of allowed types. */1592 wp_sprintf( __( 'The "type" schema keyword can only be on of the built-in types: %l.' ), $allowed_types ),1744 /* translators: 1. Parameter. 2. The list of allowed types. */ 1745 wp_sprintf( __( 'The "type" schema keyword for %1$s can only be on of the built-in types: %2$l.' ), $param, $allowed_types ), 1593 1746 '5.5.0' 1594 1747 ); … … 1596 1749 1597 1750 if ( 'array' === $args['type'] ) { 1751 $value = rest_sanitize_array( $value ); 1752 1598 1753 if ( empty( $args['items'] ) ) { 1599 return (array) $value; 1600 } 1601 1602 $value = wp_parse_list( $value ); 1754 return $value; 1755 } 1756 1603 1757 foreach ( $value as $index => $v ) { 1604 $value[ $index ] = rest_sanitize_value_from_schema( $v, $args['items'] ); 1605 } 1606 1607 // Normalize to numeric array so nothing unexpected is in the keys. 1608 $value = array_values( $value ); 1758 $value[ $index ] = rest_sanitize_value_from_schema( $v, $args['items'], $param . '[' . $index . ']' ); 1759 } 1760 1609 1761 return $value; 1610 1762 } 1611 1763 1612 1764 if ( 'object' === $args['type'] ) { 1613 if ( $value instanceof stdClass ) { 1614 $value = (array) $value; 1615 } 1616 1617 if ( $value instanceof JsonSerializable ) { 1618 $value = $value->jsonSerialize(); 1619 } 1620 1621 if ( ! is_array( $value ) ) { 1622 return array(); 1623 } 1765 $value = rest_sanitize_object( $value ); 1624 1766 1625 1767 foreach ( $value as $property => $v ) { 1626 1768 if ( isset( $args['properties'][ $property ] ) ) { 1627 $value[ $property ] = rest_sanitize_value_from_schema( $v, $args['properties'][ $property ] );1769 $value[ $property ] = rest_sanitize_value_from_schema( $v, $args['properties'][ $property ], $param . '[' . $property . ']' ); 1628 1770 } elseif ( isset( $args['additionalProperties'] ) ) { 1629 1771 if ( false === $args['additionalProperties'] ) { 1630 1772 unset( $value[ $property ] ); 1631 1773 } elseif ( is_array( $args['additionalProperties'] ) ) { 1632 $value[ $property ] = rest_sanitize_value_from_schema( $v, $args['additionalProperties'] );1774 $value[ $property ] = rest_sanitize_value_from_schema( $v, $args['additionalProperties'], $param . '[' . $property . ']' ); 1633 1775 } 1634 1776 } -
trunk/tests/phpunit/tests/rest-api.php
r48273 r48306 9 9 require_once ABSPATH . 'wp-admin/includes/admin.php'; 10 10 require_once ABSPATH . WPINC . '/rest-api.php'; 11 require_once __DIR__ . '/../includes/class-jsonserializable-object.php'; 11 12 12 13 /** … … 1458 1459 $this->assertEquals( '/wp/v2/tags/' . $term->term_id, rest_get_route_for_term( $term->term_id ) ); 1459 1460 } 1461 1462 /** 1463 * @dataProvider _dp_rest_is_object 1464 * @ticket 50300 1465 * @param bool $expected Expected result of the check. 1466 * @param mixed $value The value to check. 1467 */ 1468 public function test_rest_is_object( $expected, $value ) { 1469 $is_object = rest_is_object( $value ); 1470 1471 if ( $expected ) { 1472 $this->assertTrue( $is_object ); 1473 } else { 1474 $this->assertFalse( $is_object ); 1475 } 1476 } 1477 1478 public function _dp_rest_is_object() { 1479 return array( 1480 array( 1481 true, 1482 '', 1483 ), 1484 array( 1485 true, 1486 new stdClass(), 1487 ), 1488 array( 1489 true, 1490 new JsonSerializable_Object( array( 'hi' => 'there' ) ), 1491 ), 1492 array( 1493 true, 1494 array( 'hi' => 'there' ), 1495 ), 1496 array( 1497 true, 1498 array(), 1499 ), 1500 array( 1501 true, 1502 array( 'a', 'b' ), 1503 ), 1504 array( 1505 false, 1506 new Basic_Object(), 1507 ), 1508 array( 1509 false, 1510 new JsonSerializable_Object( 'str' ), 1511 ), 1512 array( 1513 false, 1514 'str', 1515 ), 1516 array( 1517 false, 1518 5, 1519 ), 1520 ); 1521 } 1522 1523 /** 1524 * @dataProvider _dp_rest_sanitize_object 1525 * @ticket 50300 1526 * @param array $expected Expected sanitized version. 1527 * @param mixed $value The value to sanitize. 1528 */ 1529 public function test_rest_sanitize_object( $expected, $value ) { 1530 $sanitized = rest_sanitize_object( $value ); 1531 $this->assertEquals( $expected, $sanitized ); 1532 } 1533 1534 public function _dp_rest_sanitize_object() { 1535 return array( 1536 array( 1537 array(), 1538 '', 1539 ), 1540 array( 1541 array( 'a' => '1' ), 1542 (object) array( 'a' => '1' ), 1543 ), 1544 array( 1545 array( 'hi' => 'there' ), 1546 new JsonSerializable_Object( array( 'hi' => 'there' ) ), 1547 ), 1548 array( 1549 array( 'hi' => 'there' ), 1550 array( 'hi' => 'there' ), 1551 ), 1552 array( 1553 array(), 1554 array(), 1555 ), 1556 array( 1557 array( 'a', 'b' ), 1558 array( 'a', 'b' ), 1559 ), 1560 array( 1561 array(), 1562 new Basic_Object(), 1563 ), 1564 array( 1565 array(), 1566 new JsonSerializable_Object( 'str' ), 1567 ), 1568 array( 1569 array(), 1570 'str', 1571 ), 1572 array( 1573 array(), 1574 5, 1575 ), 1576 ); 1577 } 1578 1579 /** 1580 * @dataProvider _dp_rest_is_array 1581 * @ticket 50300 1582 * @param bool $expected Expected result of the check. 1583 * @param mixed $value The value to check. 1584 */ 1585 public function test_rest_is_array( $expected, $value ) { 1586 $is_array = rest_is_array( $value ); 1587 1588 if ( $expected ) { 1589 $this->assertTrue( $is_array ); 1590 } else { 1591 $this->assertFalse( $is_array ); 1592 } 1593 } 1594 1595 public function _dp_rest_is_array() { 1596 return array( 1597 array( 1598 true, 1599 '', 1600 ), 1601 array( 1602 true, 1603 array( 'a', 'b' ), 1604 ), 1605 array( 1606 true, 1607 array(), 1608 ), 1609 array( 1610 true, 1611 'a,b,c', 1612 ), 1613 array( 1614 true, 1615 'a', 1616 ), 1617 array( 1618 true, 1619 5, 1620 ), 1621 array( 1622 false, 1623 new stdClass(), 1624 ), 1625 array( 1626 false, 1627 new JsonSerializable_Object( array( 'hi' => 'there' ) ), 1628 ), 1629 array( 1630 false, 1631 array( 'hi' => 'there' ), 1632 ), 1633 array( 1634 false, 1635 new Basic_Object(), 1636 ), 1637 array( 1638 false, 1639 new JsonSerializable_Object( 'str' ), 1640 ), 1641 array( 1642 false, 1643 null, 1644 ), 1645 ); 1646 } 1647 1648 /** 1649 * @dataProvider _dp_rest_sanitize_array 1650 * @ticket 50300 1651 * @param array $expected Expected sanitized version. 1652 * @param mixed $value The value to sanitize. 1653 */ 1654 public function test_rest_sanitize_array( $expected, $value ) { 1655 $sanitized = rest_sanitize_array( $value ); 1656 $this->assertEquals( $expected, $sanitized ); 1657 } 1658 1659 public function _dp_rest_sanitize_array() { 1660 return array( 1661 array( 1662 array(), 1663 '', 1664 ), 1665 array( 1666 array( 'a', 'b' ), 1667 array( 'a', 'b' ), 1668 ), 1669 array( 1670 array(), 1671 array(), 1672 ), 1673 array( 1674 array( 'a', 'b', 'c' ), 1675 'a,b,c', 1676 ), 1677 array( 1678 array( 'a' ), 1679 'a', 1680 ), 1681 array( 1682 array( 'a', 'b' ), 1683 'a,b,', 1684 ), 1685 array( 1686 array( '5' ), 1687 5, 1688 ), 1689 array( 1690 array(), 1691 new stdClass(), 1692 ), 1693 array( 1694 array(), 1695 new JsonSerializable_Object( array( 'hi' => 'there' ) ), 1696 ), 1697 array( 1698 array( 'there' ), 1699 array( 'hi' => 'there' ), 1700 ), 1701 array( 1702 array(), 1703 new Basic_Object(), 1704 ), 1705 array( 1706 array(), 1707 new JsonSerializable_Object( 'str' ), 1708 ), 1709 array( 1710 array(), 1711 null, 1712 ), 1713 ); 1714 } 1715 1716 /** 1717 * @dataProvider _dp_get_best_type_for_value 1718 * @ticket 50300 1719 * @param string $expected The expected best type. 1720 * @param mixed $value The value to test. 1721 * @param array $types The list of available types. 1722 */ 1723 public function test_get_best_type_for_value( $expected, $value, $types ) { 1724 $this->assertEquals( $expected, rest_get_best_type_for_value( $value, $types ) ); 1725 } 1726 1727 public function _dp_get_best_type_for_value() { 1728 return array( 1729 array( 1730 'array', 1731 array( 'hi' ), 1732 array( 'array' ), 1733 ), 1734 array( 1735 'object', 1736 array( 'hi' => 'there' ), 1737 array( 'object' ), 1738 ), 1739 array( 1740 'integer', 1741 5, 1742 array( 'integer' ), 1743 ), 1744 array( 1745 'number', 1746 4.0, 1747 array( 'number' ), 1748 ), 1749 array( 1750 'boolean', 1751 true, 1752 array( 'boolean' ), 1753 ), 1754 array( 1755 'string', 1756 'str', 1757 array( 'string' ), 1758 ), 1759 array( 1760 'null', 1761 null, 1762 array( 'null' ), 1763 ), 1764 array( 1765 'string', 1766 '', 1767 array( 'array', 'string' ), 1768 ), 1769 array( 1770 'string', 1771 '', 1772 array( 'object', 'string' ), 1773 ), 1774 array( 1775 'string', 1776 'Hello', 1777 array( 'object', 'string' ), 1778 ), 1779 array( 1780 'object', 1781 array( 'hello' => 'world' ), 1782 array( 'object', 'string' ), 1783 ), 1784 array( 1785 'number', 1786 '5.0', 1787 array( 'number', 'string' ), 1788 ), 1789 array( 1790 'string', 1791 '5.0', 1792 array( 'string', 'number' ), 1793 ), 1794 array( 1795 'boolean', 1796 'false', 1797 array( 'boolean', 'string' ), 1798 ), 1799 array( 1800 'string', 1801 'false', 1802 array( 'string', 'boolean' ), 1803 ), 1804 array( 1805 'string', 1806 'a,b', 1807 array( 'string', 'array' ), 1808 ), 1809 array( 1810 'array', 1811 'a,b', 1812 array( 'array', 'string' ), 1813 ), 1814 ); 1815 } 1460 1816 } -
trunk/tests/phpunit/tests/rest-api/rest-schema-sanitization.php
r48300 r48306 342 342 $this->assertNull( rest_sanitize_value_from_schema( null, $schema ) ); 343 343 $this->assertEquals( '2019-09-19T18:00:00', rest_sanitize_value_from_schema( '2019-09-19T18:00:00', $schema ) ); 344 $this->assert Null(rest_sanitize_value_from_schema( 'lalala', $schema ) );344 $this->assertEquals( 'lalala', rest_sanitize_value_from_schema( 'lalala', $schema ) ); 345 345 } 346 346 … … 395 395 $this->assertEquals( 'My Value', rest_sanitize_value_from_schema( 'My Value', $schema ) ); 396 396 $this->assertEquals( array( 'raw' => 'My Value' ), rest_sanitize_value_from_schema( array( 'raw' => 'My Value' ), $schema ) ); 397 $this->assert Null(rest_sanitize_value_from_schema( array( 'raw' => 1 ), $schema ) );397 $this->assertEquals( array( 'raw' => '1' ), rest_sanitize_value_from_schema( array( 'raw' => 1 ), $schema ) ); 398 398 } 399 399 … … 424 424 $this->assertEquals( array( 'raw' => false ), rest_sanitize_value_from_schema( array( 'raw' => 0 ), $schema ) ); 425 425 426 $this->assertNull( rest_sanitize_value_from_schema( array( 'raw' => 'something non boolean' ), $schema ) ); 426 $this->assertEquals( array( 'raw' => true ), rest_sanitize_value_from_schema( array( 'raw' => 'something non boolean' ), $schema ) ); 427 } 428 429 /** 430 * @ticket 50300 431 */ 432 public function test_multi_type_with_no_known_types() { 433 $this->setExpectedIncorrectUsage( 'rest_handle_multi_type_schema' ); 434 $this->setExpectedIncorrectUsage( 'rest_sanitize_value_from_schema' ); 435 436 $schema = array( 437 'type' => array( 'invalid', 'type' ), 438 ); 439 440 $this->assertEquals( 'My Value', rest_sanitize_value_from_schema( 'My Value', $schema ) ); 441 } 442 443 /** 444 * @ticket 50300 445 */ 446 public function test_multi_type_with_some_unknown_types() { 447 $this->setExpectedIncorrectUsage( 'rest_handle_multi_type_schema' ); 448 $this->setExpectedIncorrectUsage( 'rest_sanitize_value_from_schema' ); 449 450 $schema = array( 451 'type' => array( 'object', 'type' ), 452 ); 453 454 $this->assertEquals( 'My Value', rest_sanitize_value_from_schema( 'My Value', $schema ) ); 455 } 456 457 /** 458 * @ticket 50300 459 */ 460 public function test_multi_type_returns_null_if_no_valid_type() { 461 $schema = array( 462 'type' => array( 'number', 'string' ), 463 ); 464 465 $this->assertNull( rest_sanitize_value_from_schema( array( 'Hello!' ), $schema ) ); 427 466 } 428 467 } -
trunk/tests/phpunit/tests/rest-api/rest-schema-validation.php
r48300 r48306 388 388 $this->assertTrue( rest_validate_value_from_schema( null, $schema ) ); 389 389 $this->assertTrue( rest_validate_value_from_schema( '2019-09-19T18:00:00', $schema ) ); 390 $this->assertWPError( rest_validate_value_from_schema( 'some random string', $schema ) ); 390 391 $error = rest_validate_value_from_schema( 'some random string', $schema ); 392 $this->assertWPError( $error ); 393 $this->assertEquals( 'Invalid date.', $error->get_error_message() ); 391 394 } 392 395 … … 403 406 $this->assertTrue( rest_validate_value_from_schema( 'My Value', $schema ) ); 404 407 $this->assertTrue( rest_validate_value_from_schema( array( 'raw' => 'My Value' ), $schema ) ); 405 $this->assertWPError( rest_validate_value_from_schema( array( 'raw' => array( 'a list' ) ), $schema ) ); 408 409 $error = rest_validate_value_from_schema( array( 'raw' => array( 'a list' ) ), $schema ); 410 $this->assertWPError( $error ); 411 $this->assertEquals( '[raw] is not of type string.', $error->get_error_message() ); 412 } 413 414 /** 415 * @ticket 50300 416 */ 417 public function test_null_or_integer() { 418 $schema = array( 419 'type' => array( 'null', 'integer' ), 420 'minimum' => 10, 421 'maximum' => 20, 422 ); 423 424 $this->assertTrue( rest_validate_value_from_schema( null, $schema ) ); 425 $this->assertTrue( rest_validate_value_from_schema( 15, $schema ) ); 426 $this->assertTrue( rest_validate_value_from_schema( '15', $schema ) ); 427 428 $error = rest_validate_value_from_schema( 30, $schema, 'param' ); 429 $this->assertWPError( $error ); 430 $this->assertEquals( 'param must be between 10 (inclusive) and 20 (inclusive)', $error->get_error_message() ); 431 } 432 433 /** 434 * @ticket 50300 435 */ 436 public function test_multi_type_with_no_known_types() { 437 $this->setExpectedIncorrectUsage( 'rest_handle_multi_type_schema' ); 438 $this->setExpectedIncorrectUsage( 'rest_validate_value_from_schema' ); 439 440 $schema = array( 441 'type' => array( 'invalid', 'type' ), 442 ); 443 444 $this->assertTrue( rest_validate_value_from_schema( 'My Value', $schema ) ); 445 } 446 447 /** 448 * @ticket 50300 449 */ 450 public function test_multi_type_with_some_unknown_types() { 451 $this->setExpectedIncorrectUsage( 'rest_handle_multi_type_schema' ); 452 $this->setExpectedIncorrectUsage( 'rest_validate_value_from_schema' ); 453 454 $schema = array( 455 'type' => array( 'object', 'type' ), 456 ); 457 458 $this->assertTrue( rest_validate_value_from_schema( 'My Value', $schema ) ); 406 459 } 407 460
Note: See TracChangeset
for help on using the changeset viewer.