<?php

class WP_REST_Request implements ArrayAccess
{
    protected $method = '';
    protected $params;
    protected $headers = array();
    protected $body = null;
    protected $route;
    protected $attributes = array();
    protected $parsed_json = false;
    protected $parsed_body = false;
    public function __construct($method = '', $route = '', $attributes = array())
    {
        $this->params = array('URL' => array(), 'GET' => array(), 'POST' => array(), 'FILES' => array(), 'JSON' => null, 'defaults' => array());
        $this->set_method($method);
        $this->set_route($route);
        $this->set_attributes($attributes);
    }
    public function get_method()
    {
        return $this->method;
    }
    public function set_method($method)
    {
        $this->method = strtoupper($method);
    }
    public function get_headers()
    {
        return $this->headers;
    }
    public static function canonicalize_header_name($key)
    {
        $key = strtolower($key);
        $key = str_replace('-', '_', $key);
        return $key;
    }
    public function get_header($key)
    {
        $key = $this->canonicalize_header_name($key);
        if (!isset($this->headers[$key])) {
            return null;
        }
        return implode(',', $this->headers[$key]);
    }
    public function get_header_as_array($key)
    {
        $key = $this->canonicalize_header_name($key);
        if (!isset($this->headers[$key])) {
            return null;
        }
        return $this->headers[$key];
    }
    public function set_header($key, $value)
    {
        $key = $this->canonicalize_header_name($key);
        $value = (array) $value;
        $this->headers[$key] = $value;
    }
    public function add_header($key, $value)
    {
        $key = $this->canonicalize_header_name($key);
        $value = (array) $value;
        if (!isset($this->headers[$key])) {
            $this->headers[$key] = array();
        }
        $this->headers[$key] = array_merge($this->headers[$key], $value);
    }
    public function remove_header($key)
    {
        $key = $this->canonicalize_header_name($key);
        unset($this->headers[$key]);
    }
    public function set_headers($headers, $override = true)
    {
        if (true === $override) {
            $this->headers = array();
        }
        foreach ($headers as $key => $value) {
            $this->set_header($key, $value);
        }
    }
    public function get_content_type()
    {
        $value = $this->get_header('content-type');
        if (empty($value)) {
            return null;
        }
        $parameters = '';
        if (strpos($value, ';')) {
            list($value, $parameters) = explode(';', $value, 2);
        }
        $value = strtolower($value);
        if (false === strpos($value, '/')) {
            return null;
        }
        list($type, $subtype) = explode('/', $value, 2);
        $data = compact('value', 'type', 'subtype', 'parameters');
        $data = array_map('trim', $data);
        return $data;
    }
    public function is_json_content_type()
    {
        $content_type = $this->get_content_type();
        return isset($content_type['value']) && wp_is_json_media_type($content_type['value']);
    }
    protected function get_parameter_order()
    {
        $order = array();
        if ($this->is_json_content_type()) {
            $order[] = 'JSON';
        }
        $this->parse_json_params();
        $body = $this->get_body();
        if ('POST' !== $this->method && !empty($body)) {
            $this->parse_body_params();
        }
        $accepts_body_data = array('POST', 'PUT', 'PATCH', 'DELETE');
        if (in_array($this->method, $accepts_body_data, true)) {
            $order[] = 'POST';
        }
        $order[] = 'GET';
        $order[] = 'URL';
        $order[] = 'defaults';
        return apply_filters('rest_request_parameter_order', $order, $this);
    }
    public function get_param($key)
    {
        $order = $this->get_parameter_order();
        foreach ($order as $type) {
            if (isset($this->params[$type][$key])) {
                return $this->params[$type][$key];
            }
        }
        return null;
    }
    public function has_param($key)
    {
        $order = $this->get_parameter_order();
        foreach ($order as $type) {
            if (is_array($this->params[$type]) && array_key_exists($key, $this->params[$type])) {
                return true;
            }
        }
        return false;
    }
    public function set_param($key, $value)
    {
        $order = $this->get_parameter_order();
        $found_key = false;
        foreach ($order as $type) {
            if ('defaults' !== $type && is_array($this->params[$type]) && array_key_exists($key, $this->params[$type])) {
                $this->params[$type][$key] = $value;
                $found_key = true;
            }
        }
        if (!$found_key) {
            $this->params[$order[0]][$key] = $value;
        }
    }
    public function get_params()
    {
        $order = $this->get_parameter_order();
        $order = array_reverse($order, true);
        $params = array();
        foreach ($order as $type) {
            foreach ((array) $this->params[$type] as $key => $value) {
                $params[$key] = $value;
            }
        }
        return $params;
    }
    public function get_url_params()
    {
        return $this->params['URL'];
    }
    public function set_url_params($params)
    {
        $this->params['URL'] = $params;
    }
    public function get_query_params()
    {
        return $this->params['GET'];
    }
    public function set_query_params($params)
    {
        $this->params['GET'] = $params;
    }
    public function get_body_params()
    {
        return $this->params['POST'];
    }
    public function set_body_params($params)
    {
        $this->params['POST'] = $params;
    }
    public function get_file_params()
    {
        return $this->params['FILES'];
    }
    public function set_file_params($params)
    {
        $this->params['FILES'] = $params;
    }
    public function get_default_params()
    {
        return $this->params['defaults'];
    }
    public function set_default_params($params)
    {
        $this->params['defaults'] = $params;
    }
    public function get_body()
    {
        return $this->body;
    }
    public function set_body($data)
    {
        $this->body = $data;
        $this->parsed_json = false;
        $this->parsed_body = false;
        $this->params['JSON'] = null;
    }
    public function get_json_params()
    {
        $this->parse_json_params();
        return $this->params['JSON'];
    }
    protected function parse_json_params()
    {
        if ($this->parsed_json) {
            return true;
        }
        $this->parsed_json = true;
        if (!$this->is_json_content_type()) {
            return true;
        }
        $body = $this->get_body();
        if (empty($body)) {
            return true;
        }
        $params = json_decode($body, true);
        if (null === $params && JSON_ERROR_NONE !== json_last_error()) {
            $this->parsed_json = false;
            $error_data = array('status' => WP_Http::BAD_REQUEST, 'json_error_code' => json_last_error(), 'json_error_message' => json_last_error_msg());
            return new WP_Error('rest_invalid_json', __('Invalid JSON body passed.'), $error_data);
        }
        $this->params['JSON'] = $params;
        return true;
    }
    protected function parse_body_params()
    {
        if ($this->parsed_body) {
            return;
        }
        $this->parsed_body = true;
        $content_type = $this->get_content_type();
        if (!empty($content_type) && 'application/x-www-form-urlencoded' !== $content_type['value']) {
            return;
        }
        parse_str($this->get_body(), $params);
        $this->params['POST'] = array_merge($params, $this->params['POST']);
    }
    public function get_route()
    {
        return $this->route;
    }
    public function set_route($route)
    {
        $this->route = $route;
    }
    public function get_attributes()
    {
        return $this->attributes;
    }
    public function set_attributes($attributes)
    {
        $this->attributes = $attributes;
    }
    public function sanitize_params()
    {
        $attributes = $this->get_attributes();
        if (empty($attributes['args'])) {
            return true;
        }
        $order = $this->get_parameter_order();
        $invalid_params = array();
        $invalid_details = array();
        foreach ($order as $type) {
            if (empty($this->params[$type])) {
                continue;
            }
            foreach ($this->params[$type] as $key => $value) {
                if (!isset($attributes['args'][$key])) {
                    continue;
                }
                $param_args = $attributes['args'][$key];
                if (!array_key_exists('sanitize_callback', $param_args) && !empty($param_args['type'])) {
                    $param_args['sanitize_callback'] = 'rest_parse_request_arg';
                }
                if (empty($param_args['sanitize_callback'])) {
                    continue;
                }
                $sanitized_value = call_user_func($param_args['sanitize_callback'], $value, $this, $key);
                if (is_wp_error($sanitized_value)) {
                    $invalid_params[$key] = implode(' ', $sanitized_value->get_error_messages());
                    $invalid_details[$key] = rest_convert_error_to_response($sanitized_value)->get_data();
                } else {
                    $this->params[$type][$key] = $sanitized_value;
                }
            }
        }
        if ($invalid_params) {
            return new WP_Error('rest_invalid_param', sprintf(__('Invalid parameter(s): %s'), implode(', ', array_keys($invalid_params))), array('status' => 400, 'params' => $invalid_params, 'details' => $invalid_details));
        }
        return true;
    }
    public function has_valid_params()
    {
        $json_error = $this->parse_json_params();
        if (is_wp_error($json_error)) {
            return $json_error;
        }
        $attributes = $this->get_attributes();
        $required = array();
        $args = empty($attributes['args']) ? array() : $attributes['args'];
        foreach ($args as $key => $arg) {
            $param = $this->get_param($key);
            if (isset($arg['required']) && true === $arg['required'] && null === $param) {
                $required[] = $key;
            }
        }
        if (!empty($required)) {
            return new WP_Error('rest_missing_callback_param', sprintf(__('Missing parameter(s): %s'), implode(', ', $required)), array('status' => 400, 'params' => $required));
        }
        $invalid_params = array();
        $invalid_details = array();
        foreach ($args as $key => $arg) {
            $param = $this->get_param($key);
            if (null !== $param && !empty($arg['validate_callback'])) {
                $valid_check = call_user_func($arg['validate_callback'], $param, $this, $key);
                if (false === $valid_check) {
                    $invalid_params[$key] = __('Invalid parameter.');
                }
                if (is_wp_error($valid_check)) {
                    $invalid_params[$key] = implode(' ', $valid_check->get_error_messages());
                    $invalid_details[$key] = rest_convert_error_to_response($valid_check)->get_data();
                }
            }
        }
        if ($invalid_params) {
            return new WP_Error('rest_invalid_param', sprintf(__('Invalid parameter(s): %s'), implode(', ', array_keys($invalid_params))), array('status' => 400, 'params' => $invalid_params, 'details' => $invalid_details));
        }
        if (isset($attributes['validate_callback'])) {
            $valid_check = call_user_func($attributes['validate_callback'], $this);
            if (is_wp_error($valid_check)) {
                return $valid_check;
            }
            if (false === $valid_check) {
                return new WP_Error('rest_invalid_params', __('Invalid parameters.'), array('status' => 400));
            }
        }
        return true;
    }
    public function offsetExists($offset)
    {
        $order = $this->get_parameter_order();
        foreach ($order as $type) {
            if (isset($this->params[$type][$offset])) {
                return true;
            }
        }
        return false;
    }
    public function offsetGet($offset)
    {
        return $this->get_param($offset);
    }
    public function offsetSet($offset, $value)
    {
        $this->set_param($offset, $value);
    }
    public function offsetUnset($offset)
    {
        $order = $this->get_parameter_order();
        foreach ($order as $type) {
            unset($this->params[$type][$offset]);
        }
    }
    public static function from_url($url)
    {
        $bits = parse_url($url);
        $query_params = array();
        if (!empty($bits['query'])) {
            wp_parse_str($bits['query'], $query_params);
        }
        $api_root = rest_url();
        if (get_option('permalink_structure') && 0 === strpos($url, $api_root)) {
            $api_url_part = substr($url, strlen(untrailingslashit($api_root)));
            $route = parse_url($api_url_part, PHP_URL_PATH);
        } elseif (!empty($query_params['rest_route'])) {
            $route = $query_params['rest_route'];
            unset($query_params['rest_route']);
        }
        $request = false;
        if (!empty($route)) {
            $request = new WP_REST_Request('GET', $route);
            $request->set_query_params($query_params);
        }
        return apply_filters('rest_request_from_url', $request, $url);
    }
}