function cast_and_filter_item(&$return, $type, $key, $value, $types = array(), $for_output = false)
{
if (is_string($type)) {
$type = compact('type');
}
switch ($type['type']) {
case 'false':
$return[$key] = false;
break;
case 'url':
$return[$key] = (string) esc_url_raw($value);
break;
case 'string':
// Fallback string -> array, or for string -> object
if (is_array($value) || is_object($value)) {
if (!empty($types[0])) {
$next_type = array_shift($types);
return $this->cast_and_filter_item($return, $next_type, $key, $value, $types, $for_output);
}
}
// Fallback string -> false
if (!is_string($value)) {
if (!empty($types[0]) && 'false' === $types[0]['type']) {
$next_type = array_shift($types);
return $this->cast_and_filter_item($return, $next_type, $key, $value, $types, $for_output);
}
}
$return[$key] = (string) $value;
break;
case 'html':
$return[$key] = (string) $value;
break;
case 'safehtml':
$return[$key] = wp_kses((string) $value, wp_kses_allowed_html());
break;
case 'zip':
case 'media':
if (is_array($value)) {
if (isset($value['name']) && is_array($value['name'])) {
// It's a $_FILES array
// Reformat into array of $_FILES items
$files = array();
foreach ($value['name'] as $k => $v) {
$files[$k] = array();
foreach (array_keys($value) as $file_key) {
$files[$k][$file_key] = $value[$file_key][$k];
}
}
$return[$key] = $files;
break;
}
} else {
// no break - treat as 'array'
}
// nobreak
// nobreak
case 'array':
// Fallback array -> string
if (is_string($value)) {
if (!empty($types[0])) {
$next_type = array_shift($types);
return $this->cast_and_filter_item($return, $next_type, $key, $value, $types, $for_output);
}
}
if (isset($type['children'])) {
$children = array();
foreach ((array) $value as $k => $child) {
$this->cast_and_filter_item($children, $type['children'], $k, $child, array(), $for_output);
}
$return[$key] = (array) $children;
break;
}
$return[$key] = (array) $value;
break;
case 'iso 8601 datetime':
case 'datetime':
// (string)s
$dates = $this->parse_date((string) $value);
if ($for_output) {
$return[$key] = $this->format_date($dates[1], $dates[0]);
} else {
list($return[$key], $return["{$key}_gmt"]) = $dates;
}
break;
case 'float':
$return[$key] = (double) $value;
break;
case 'int':
case 'integer':
$return[$key] = (int) $value;
break;
case 'bool':
case 'boolean':
$return[$key] = (bool) WPCOM_JSON_API::is_truthy($value);
break;
case 'object':
// Fallback object -> false
if (is_scalar($value) || is_null($value)) {
if (!empty($types[0]) && 'false' === $types[0]['type']) {
return $this->cast_and_filter_item($return, 'false', $key, $value, $types, $for_output);
}
}
if (isset($type['children'])) {
$children = array();
foreach ((array) $value as $k => $child) {
$this->cast_and_filter_item($children, $type['children'], $k, $child, array(), $for_output);
}
$return[$key] = (object) $children;
break;
}
if (isset($type['subtype'])) {
return $this->cast_and_filter_item($return, $type['subtype'], $key, $value, $types, $for_output);
}
$return[$key] = (object) $value;
break;
case 'post':
$return[$key] = (object) $this->cast_and_filter($value, $this->post_object_format, false, $for_output);
break;
case 'comment':
$return[$key] = (object) $this->cast_and_filter($value, $this->comment_object_format, false, $for_output);
break;
case 'tag':
case 'category':
$docs = array('ID' => '(int)', 'name' => '(string)', 'slug' => '(string)', 'description' => '(HTML)', 'post_count' => '(int)', 'meta' => '(object)');
if ('category' === $type['type']) {
$docs['parent'] = '(int)';
}
$return[$key] = (object) $this->cast_and_filter($value, $docs, false, $for_output);
break;
case 'post_reference':
case 'comment_reference':
$docs = array('ID' => '(int)', 'type' => '(string)', 'title' => '(string)', 'link' => '(URL)');
$return[$key] = (object) $this->cast_and_filter($value, $docs, false, $for_output);
break;
case 'geo':
$docs = array('latitude' => '(float)', 'longitude' => '(float)', 'address' => '(string)');
$return[$key] = (object) $this->cast_and_filter($value, $docs, false, $for_output);
break;
case 'author':
$docs = array('ID' => '(int)', 'user_login' => '(string)', 'login' => '(string)', 'email' => '(string|false)', 'name' => '(string)', 'first_name' => '(string)', 'last_name' => '(string)', 'nice_name' => '(string)', 'URL' => '(URL)', 'avatar_URL' => '(URL)', 'profile_URL' => '(URL)', 'is_super_admin' => '(bool)', 'roles' => '(array:string)');
$return[$key] = (object) $this->cast_and_filter($value, $docs, false, $for_output);
break;
case 'role':
$docs = array('name' => '(string)', 'display_name' => '(string)', 'capabilities' => '(object:boolean)');
$return[$key] = (object) $this->cast_and_filter($value, $docs, false, $for_output);
break;
case 'attachment':
$docs = array('ID' => '(int)', 'URL' => '(URL)', 'guid' => '(string)', 'mime_type' => '(string)', 'width' => '(int)', 'height' => '(int)', 'duration' => '(int)');
$return[$key] = (object) $this->cast_and_filter($value, apply_filters('wpcom_json_api_attachment_cast_and_filter', $docs), false, $for_output);
break;
case 'metadata':
$docs = array('id' => '(int)', 'key' => '(string)', 'value' => '(string|false|float|int|array|object)', 'previous_value' => '(string)', 'operation' => '(string)');
$return[$key] = (object) $this->cast_and_filter($value, apply_filters('wpcom_json_api_attachment_cast_and_filter', $docs), false, $for_output);
break;
case 'plugin':
$docs = array('id' => '(safehtml) The plugin\'s ID', 'slug' => '(safehtml) The plugin\'s Slug', 'active' => '(boolean) The plugin status.', 'update' => '(object) The plugin update info.', 'name' => '(safehtml) The name of the plugin.', 'plugin_url' => '(url) Link to the plugin\'s web site.', 'version' => '(safehtml) The plugin version number.', 'description' => '(safehtml) Description of what the plugin does and/or notes from the author', 'author' => '(safehtml) The plugin author\'s name', 'author_url' => '(url) The plugin author web site address', 'network' => '(boolean) Whether the plugin can only be activated network wide.', 'autoupdate' => '(boolean) Whether the plugin is auto updated', 'log' => '(array:safehtml) An array of update log strings.');
$return[$key] = (object) $this->cast_and_filter($value, apply_filters('wpcom_json_api_plugin_cast_and_filter', $docs), false, $for_output);
break;
case 'jetpackmodule':
$docs = array('id' => '(string) The module\'s ID', 'active' => '(boolean) The module\'s status.', 'name' => '(string) The module\'s name.', 'description' => '(safehtml) The module\'s description.', 'sort' => '(int) The module\'s display order.', 'introduced' => '(string) The Jetpack version when the module was introduced.', 'changed' => '(string) The Jetpack version when the module was changed.', 'free' => '(boolean) The module\'s Free or Paid status.', 'module_tags' => '(array) The module\'s tags.');
$return[$key] = (object) $this->cast_and_filter($value, apply_filters('wpcom_json_api_plugin_cast_and_filter', $docs), false, $for_output);
break;
case 'sharing_button':
$docs = array('ID' => '(string)', 'name' => '(string)', 'URL' => '(string)', 'icon' => '(string)', 'enabled' => '(bool)', 'visibility' => '(string)');
$return[$key] = (array) $this->cast_and_filter($value, $docs, false, $for_output);
break;
case 'sharing_button_service':
$docs = array('ID' => '(string) The service identifier', 'name' => '(string) The service name', 'class_name' => '(string) Class name for custom style sharing button elements', 'genericon' => '(string) The Genericon unicode character for the custom style sharing button icon', 'preview_smart' => '(string) An HTML snippet of a rendered sharing button smart preview', 'preview_smart_js' => '(string) An HTML snippet of the page-wide initialization scripts used for rendering the sharing button smart preview');
$return[$key] = (array) $this->cast_and_filter($value, $docs, false, $for_output);
break;
case 'taxonomy':
$docs = array('name' => '(string) The taxonomy slug', 'label' => '(string) The taxonomy human-readable name', 'labels' => '(object) Mapping of labels for the taxonomy', 'description' => '(string) The taxonomy description', 'hierarchical' => '(bool) Whether the taxonomy is hierarchical', 'public' => '(bool) Whether the taxonomy is public', 'capabilities' => '(object) Mapping of current user capabilities for the taxonomy');
$return[$key] = (array) $this->cast_and_filter($value, $docs, false, $for_output);
break;
default:
$method_name = $type['type'] . '_docs';
if (method_exists(WPCOM_JSON_API_Jetpack_Overrides, $method_name)) {
$docs = WPCOM_JSON_API_Jetpack_Overrides::$method_name();
}
if (!empty($docs)) {
$return[$key] = (object) $this->cast_and_filter($value, apply_filters('wpcom_json_api_plugin_cast_and_filter', $docs), false, $for_output);
} else {
trigger_error("Unknown API casting type {$type['type']}", E_USER_WARNING);
}
}
}