Add new WC_API_Server class
Based on WP_JSON_Server, this class add response handling based on endpoint suffixes (.json or .xml) or the ACCEPT header. It also removes some unneeded functionality and renames filters so as to not conflict with users who may have WP-API installed already. Finally, instead of no authentication required by default, authentication is always required unless specifically disabled via filters.
This commit is contained in:
parent
8637da63c0
commit
ed6de29bfc
|
@ -0,0 +1,700 @@
|
|||
<?php
|
||||
/**
|
||||
* WooCommerce API
|
||||
*
|
||||
* Handles REST API requests
|
||||
*
|
||||
* This class and related code (JSON response handler, resource classes) are based on WP-API v0.6 (https://github.com/WP-API/WP-API)
|
||||
* Many thanks to Ryan McCue and any other contributors!
|
||||
*
|
||||
* @author WooThemes
|
||||
* @category API
|
||||
* @package WooCommerce/API
|
||||
* @since 2.1
|
||||
*/
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
||||
|
||||
require_once ABSPATH . 'wp-admin/includes/admin.php';
|
||||
|
||||
class WC_API_Server {
|
||||
|
||||
const METHOD_GET = 1;
|
||||
const METHOD_POST = 2;
|
||||
const METHOD_PUT = 4;
|
||||
const METHOD_PATCH = 8;
|
||||
const METHOD_DELETE = 16;
|
||||
|
||||
const READABLE = 1; // GET
|
||||
const CREATABLE = 2; // POST
|
||||
const EDITABLE = 14; // POST | PUT | PATCH
|
||||
const DELETABLE = 16; // DELETE
|
||||
const ALLMETHODS = 31; // GET | POST | PUT | PATCH | DELETE
|
||||
|
||||
/**
|
||||
* Does the endpoint accept a raw request body?
|
||||
*/
|
||||
const ACCEPT_RAW_DATA = 64;
|
||||
|
||||
/** Does the endpoint accept a request body? (either JSON or XML) */
|
||||
const ACCEPT_DATA = 128;
|
||||
|
||||
/**
|
||||
* Should we hide this endpoint from the index?
|
||||
*/
|
||||
const HIDDEN_ENDPOINT = 256;
|
||||
|
||||
/**
|
||||
* Map of HTTP verbs to constants
|
||||
* @var array
|
||||
*/
|
||||
public static $method_map = array(
|
||||
'HEAD' => self::METHOD_GET,
|
||||
'GET' => self::METHOD_GET,
|
||||
'POST' => self::METHOD_POST,
|
||||
'PUT' => self::METHOD_PUT,
|
||||
'PATCH' => self::METHOD_PATCH,
|
||||
'DELETE' => self::METHOD_DELETE,
|
||||
);
|
||||
|
||||
/**
|
||||
* Requested path (relative to the API root, wp-json.php)
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $path = '';
|
||||
|
||||
/**
|
||||
* Requested method (GET/HEAD/POST/PUT/PATCH/DELETE)
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $method = 'HEAD';
|
||||
|
||||
/**
|
||||
* Request parameters
|
||||
*
|
||||
* This acts as an abstraction of the superglobals
|
||||
* (GET => $_GET, POST => $_POST)
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $params = array( 'GET' => array(), 'POST' => array() );
|
||||
|
||||
/**
|
||||
* Request headers
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $headers = array();
|
||||
|
||||
/**
|
||||
* Request files (matches $_FILES)
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $files = array();
|
||||
|
||||
/**
|
||||
* Request/Response handler, either JSON by default
|
||||
* or XML if requested by client
|
||||
*
|
||||
* @var WC_API_Handler
|
||||
*/
|
||||
public $handler;
|
||||
|
||||
|
||||
/**
|
||||
* Setup class and set request/response handler
|
||||
*
|
||||
* @since 2.1
|
||||
* @param $path
|
||||
* @return WC_API_Server
|
||||
*/
|
||||
public function __construct( $path ) {
|
||||
|
||||
if ( empty( $path ) ) {
|
||||
if ( isset( $_SERVER['PATH_INFO'] ) )
|
||||
$path = $_SERVER['PATH_INFO'];
|
||||
else
|
||||
$path = '/';
|
||||
}
|
||||
|
||||
$this->path = $path;
|
||||
$this->method = $_SERVER['REQUEST_METHOD'];
|
||||
$this->params['GET'] = $_GET;
|
||||
$this->params['POST'] = $_POST;
|
||||
$this->headers = $this->get_headers( $_SERVER );
|
||||
$this->files = $_FILES;
|
||||
|
||||
// Compatibility for clients that can't use PUT/PATCH/DELETE
|
||||
if ( isset( $_GET['_method'] ) ) {
|
||||
$this->method = strtoupper( $_GET['_method'] );
|
||||
}
|
||||
|
||||
// determine type of request/response and load handler, JSON by default
|
||||
if ( $this->is_json_request() )
|
||||
$handler_class = 'WC_API_JSON_Handler';
|
||||
|
||||
elseif ( $this->is_xml_request() )
|
||||
$handler_class = 'WC_API_XML_Handler';
|
||||
|
||||
else
|
||||
$handler_class = apply_filters( 'woocommerce_api_default_response_handler', 'WC_API_JSON_Handler', $this->path, $this );
|
||||
|
||||
$this->handler = new $handler_class();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check authentication for the request
|
||||
*
|
||||
* @since 2.1
|
||||
* @return WP_User|WP_Error WP_User object indicates successful login, WP_Error indicates unsuccessful login
|
||||
*/
|
||||
public function check_authentication() {
|
||||
|
||||
// allow plugins to remove default authentication or add their own authentication
|
||||
$user = apply_filters( 'woocommerce_api_check_authentication', null, $this );
|
||||
|
||||
// API requests run under the context of the authenticated user
|
||||
if ( is_a( $user, 'WP_User' ) )
|
||||
wp_set_current_user( $user->ID );
|
||||
|
||||
// WP_Errors are handled in serve_request()
|
||||
elseif ( ! is_wp_error( $user ) )
|
||||
$user = new WP_Error( 'woocommerce_api_authentication_error', __( 'Invalid authentication method', 'woocommerce' ), array( 'code' => '500' ) );
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an error to an array
|
||||
*
|
||||
* This iterates over all error codes and messages to change it into a flat
|
||||
* array. This enables simpler client behaviour, as it is represented as a
|
||||
* list in JSON rather than an object/map
|
||||
*
|
||||
* @since 2.1
|
||||
* @param WP_Error $error
|
||||
* @return array List of associative arrays with code and message keys
|
||||
*/
|
||||
protected function error_to_array( $error ) {
|
||||
$errors = array();
|
||||
foreach ( (array) $error->errors as $code => $messages ) {
|
||||
foreach ( (array) $messages as $message ) {
|
||||
$errors[] = array( 'code' => $code, 'message' => $message );
|
||||
}
|
||||
}
|
||||
return $errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle serving an API request
|
||||
*
|
||||
* Matches the current server URI to a route and runs the first matching
|
||||
* callback then outputs a JSON representation of the returned value.
|
||||
*
|
||||
* @since 2.1
|
||||
* @uses WC_API_Server::dispatch()
|
||||
*/
|
||||
public function serve_request() {
|
||||
|
||||
do_action( 'woocommerce_api_server_before_serve', $this );
|
||||
|
||||
$this->header( 'Content-Type', $this->handler->get_content_type(), true );
|
||||
|
||||
// TODO: can we prevent wc_cookie from being sent for API requests?
|
||||
|
||||
// the API is enabled by default TODO: implement check for enabled setting here
|
||||
if ( ! apply_filters( 'woocommerce_api_enabled', true, $this ) ) {
|
||||
|
||||
$this->send_status( 404 );
|
||||
|
||||
echo $this->handler->generate_response( array( array( 'code' => 'woocommerce_api_disabled', 'message' => 'The WooCommerce API is disabled on this site' ) ) );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$result = $this->check_authentication();
|
||||
|
||||
// if authorization check was successful, dispatch the request
|
||||
if ( ! is_wp_error( $result ) ) {
|
||||
$result = $this->dispatch();
|
||||
}
|
||||
|
||||
// handle any dispatch errors
|
||||
if ( is_wp_error( $result ) ) {
|
||||
$data = $result->get_error_data();
|
||||
if ( is_array( $data ) && isset( $data['status'] ) ) {
|
||||
$this->send_status( $data['status'] );
|
||||
}
|
||||
|
||||
$result = $this->error_to_array( $result );
|
||||
}
|
||||
|
||||
// This is a filter rather than an action, since this is designed to be
|
||||
// re-entrant if needed
|
||||
$served = apply_filters( 'woocommerce_api_serve_request', false, $result, $this );
|
||||
|
||||
if ( ! $served ) {
|
||||
|
||||
if ( 'HEAD' === $this->method )
|
||||
return;
|
||||
|
||||
echo $this->handler->generate_response( $result );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the route map
|
||||
*
|
||||
* The route map is an associative array with path regexes as the keys. The
|
||||
* value is an indexed array with the callback function/method as the first
|
||||
* item, and a bitmask of HTTP methods as the second item (see the class
|
||||
* constants).
|
||||
*
|
||||
* Each route can be mapped to more than one callback by using an array of
|
||||
* the indexed arrays. This allows mapping e.g. GET requests to one callback
|
||||
* and POST requests to another.
|
||||
*
|
||||
* Note that the path regexes (array keys) must have @ escaped, as this is
|
||||
* used as the delimiter with preg_match()
|
||||
*
|
||||
* @since 2.1
|
||||
* @return array `'/path/regex' => array( $callback, $bitmask )` or `'/path/regex' => array( array( $callback, $bitmask ), ...)`
|
||||
*/
|
||||
public function get_routes() {
|
||||
|
||||
// index added by default
|
||||
$endpoints = array(
|
||||
|
||||
'/' => array( array( $this, 'get_index' ), self::READABLE ),
|
||||
);
|
||||
|
||||
$endpoints = apply_filters( 'woocommerce_api_endpoints', $endpoints );
|
||||
|
||||
// Normalise the endpoints
|
||||
foreach ( $endpoints as $route => &$handlers ) {
|
||||
if ( count( $handlers ) <= 2 && isset( $handlers[1] ) && ! is_array( $handlers[1] ) ) {
|
||||
$handlers = array( $handlers );
|
||||
}
|
||||
}
|
||||
|
||||
return $endpoints;
|
||||
}
|
||||
|
||||
/**
|
||||
* Match the request to a callback and call it
|
||||
*
|
||||
* @since 2.1
|
||||
* @return mixed The value returned by the callback, or a WP_Error instance
|
||||
*/
|
||||
public function dispatch() {
|
||||
|
||||
switch ( $this->method ) {
|
||||
|
||||
case 'HEAD':
|
||||
case 'GET':
|
||||
$method = self::METHOD_GET;
|
||||
break;
|
||||
|
||||
case 'POST':
|
||||
$method = self::METHOD_POST;
|
||||
break;
|
||||
|
||||
case 'PUT':
|
||||
$method = self::METHOD_PUT;
|
||||
break;
|
||||
|
||||
case 'PATCH':
|
||||
$method = self::METHOD_PATCH;
|
||||
break;
|
||||
|
||||
case 'DELETE':
|
||||
$method = self::METHOD_DELETE;
|
||||
break;
|
||||
|
||||
default:
|
||||
return new WP_Error( 'woocommerce_api_unsupported_method', __( 'Unsupported request method' ), array( 'status' => 400 ) );
|
||||
}
|
||||
|
||||
foreach ( $this->get_routes() as $route => $handlers ) {
|
||||
foreach ( $handlers as $handler ) {
|
||||
$callback = $handler[0];
|
||||
$supported = isset( $handler[1] ) ? $handler[1] : self::METHOD_GET;
|
||||
|
||||
if ( !( $supported & $method ) )
|
||||
continue;
|
||||
|
||||
$match = preg_match( '@^' . $route . '$@i', $this->path, $args );
|
||||
|
||||
if ( !$match )
|
||||
continue;
|
||||
|
||||
if ( ! is_callable( $callback ) )
|
||||
return new WP_Error( 'woocommerce_api_invalid_handler', __( 'The handler for the route is invalid' ), array( 'status' => 500 ) );
|
||||
|
||||
$args = array_merge( $args, $this->params['GET'] );
|
||||
if ( $method & self::METHOD_POST ) {
|
||||
$args = array_merge( $args, $this->params['POST'] );
|
||||
}
|
||||
if ( $supported & self::ACCEPT_DATA ) {
|
||||
$data = $this->handler->parse_body( $this->get_raw_data() );
|
||||
$args = array_merge( $args, array( 'data' => $data ) );
|
||||
}
|
||||
elseif ( $supported & self::ACCEPT_RAW_DATA ) {
|
||||
$data = $this->get_raw_data();
|
||||
$args = array_merge( $args, array( 'data' => $data ) );
|
||||
}
|
||||
|
||||
$args['_method'] = $method;
|
||||
$args['_route'] = $route;
|
||||
$args['_path'] = $this->path;
|
||||
$args['_headers'] = $this->headers;
|
||||
$args['_files'] = $this->files;
|
||||
|
||||
$args = apply_filters( 'woocommerce_api_dispatch_args', $args, $callback );
|
||||
|
||||
// Allow plugins to halt the request via this filter
|
||||
if ( is_wp_error( $args ) ) {
|
||||
return $args;
|
||||
}
|
||||
|
||||
$params = $this->sort_callback_params( $callback, $args );
|
||||
if ( is_wp_error( $params ) )
|
||||
return $params;
|
||||
|
||||
return call_user_func_array( $callback, $params );
|
||||
}
|
||||
}
|
||||
|
||||
return new WP_Error( 'woocommerce_api_no_route', __( 'No route was found matching the URL and request method' ), array( 'status' => 404 ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort parameters by order specified in method declaration
|
||||
*
|
||||
* Takes a callback and a list of available params, then filters and sorts
|
||||
* by the parameters the method actually needs, using the Reflection API
|
||||
*
|
||||
* @since 2.1
|
||||
* @param array $callback the endpoint callback
|
||||
* @param array $provided the provided request parameters
|
||||
* @return array
|
||||
*/
|
||||
protected function sort_callback_params( $callback, $provided ) {
|
||||
if ( is_array( $callback ) )
|
||||
$ref_func = new ReflectionMethod( $callback[0], $callback[1] );
|
||||
else
|
||||
$ref_func = new ReflectionFunction( $callback );
|
||||
|
||||
$wanted = $ref_func->getParameters();
|
||||
$ordered_parameters = array();
|
||||
|
||||
foreach ( $wanted as $param ) {
|
||||
if ( isset( $provided[ $param->getName() ] ) ) {
|
||||
// We have this parameters in the list to choose from
|
||||
$ordered_parameters[] = $provided[ $param->getName() ];
|
||||
}
|
||||
elseif ( $param->isDefaultValueAvailable() ) {
|
||||
// We don't have this parameter, but it's optional
|
||||
$ordered_parameters[] = $param->getDefaultValue();
|
||||
}
|
||||
else {
|
||||
// We don't have this parameter and it wasn't optional, abort!
|
||||
return new WP_Error( 'woocommerce_api_missing_callback_param', sprintf( __( 'Missing parameter %s' ), $param->getName() ), array( 'status' => 400 ) );
|
||||
}
|
||||
}
|
||||
return $ordered_parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the site index.
|
||||
*
|
||||
* This endpoint describes the capabilities of the site.
|
||||
*
|
||||
* @since 2.1
|
||||
* @return array Index entity
|
||||
*/
|
||||
public function get_index() {
|
||||
|
||||
// General site data
|
||||
$available = array(
|
||||
'name' => get_option( 'blogname' ),
|
||||
'description' => get_option( 'blogdescription' ),
|
||||
'URL' => get_option( 'siteurl' ),
|
||||
'routes' => array(),
|
||||
'meta' => array(
|
||||
'currency' => get_woocommerce_currency(),
|
||||
'weight_unit' => get_option( 'woocommerce_weight_unit' ),
|
||||
'dimension_unit' => get_option( 'woocommerce_dimension_unit' ),
|
||||
'supports_ssl' => ( 'yes' === get_option( 'woocommerce_force_ssl_checkout' ) ),
|
||||
'links' => array(
|
||||
'help' => 'http://docs.woothemes.com/document/woocommerce-rest-api/',
|
||||
'profile' => 'https://raw.github.com/rmccue/WP-API/master/docs/schema.json', // TODO: update this
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
// Find the available routes
|
||||
foreach ( $this->get_routes() as $route => $callbacks ) {
|
||||
$data = array();
|
||||
|
||||
$route = preg_replace( '#\(\?P(<\w+?>).*?\)#', '$1', $route );
|
||||
$methods = array();
|
||||
foreach ( self::$method_map as $name => $bitmask ) {
|
||||
foreach ( $callbacks as $callback ) {
|
||||
// Skip to the next route if any callback is hidden
|
||||
if ( $callback[1] & self::HIDDEN_ENDPOINT )
|
||||
continue 3;
|
||||
|
||||
if ( $callback[1] & $bitmask )
|
||||
$data['supports'][] = $name;
|
||||
|
||||
if ( $callback[1] & self::ACCEPT_DATA )
|
||||
$data['accepts_data'] = true;
|
||||
|
||||
// For non-variable routes, generate links
|
||||
if ( strpos( $route, '<' ) === false ) {
|
||||
$data['meta'] = array(
|
||||
'self' => get_woocommerce_api_url( $route ),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
$available['routes'][$route] = apply_filters( 'woocommerce_api_endpoints_description', $data );
|
||||
}
|
||||
return apply_filters( 'woocommerce_api_index', $available );
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a HTTP status code
|
||||
*
|
||||
* @since 2.1
|
||||
* @param int $code HTTP status
|
||||
*/
|
||||
public function send_status( $code ) {
|
||||
status_header( $code );
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a HTTP header
|
||||
*
|
||||
* @since 2.1
|
||||
* @param string $key Header key
|
||||
* @param string $value Header value
|
||||
* @param boolean $replace Should we replace the existing header?
|
||||
*/
|
||||
public function header( $key, $value, $replace = true ) {
|
||||
header( sprintf( '%s: %s', $key, $value ), $replace );
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a Link header
|
||||
*
|
||||
* @internal The $rel parameter is first, as this looks nicer when sending multiple
|
||||
*
|
||||
* @link http://tools.ietf.org/html/rfc5988
|
||||
* @link http://www.iana.org/assignments/link-relations/link-relations.xml
|
||||
*
|
||||
* @since 2.1
|
||||
* @param string $rel Link relation. Either a registered type, or an absolute URL
|
||||
* @param string $link Target IRI for the link
|
||||
* @param array $other Other parameters to send, as an assocative array
|
||||
*/
|
||||
public function link_header( $rel, $link, $other = array() ) {
|
||||
$header = 'Link: <' . $link . '>; rel="' . $rel . '"';
|
||||
foreach ( $other as $key => $value ) {
|
||||
if ( 'title' == $key )
|
||||
$value = '"' . $value . '"';
|
||||
$header .= '; ' . $key . '=' . $value;
|
||||
}
|
||||
$this->header( 'Link', $header, false );
|
||||
}
|
||||
|
||||
/**
|
||||
* Send navigation-related headers for post collections
|
||||
*
|
||||
* @since 2.1
|
||||
* @param WP_Query $query
|
||||
*/
|
||||
public function query_navigation_headers( $query ) {
|
||||
$max_page = $query->max_num_pages;
|
||||
$paged = $query->get('paged');
|
||||
|
||||
if ( !$paged )
|
||||
$paged = 1;
|
||||
|
||||
$nextpage = intval($paged) + 1;
|
||||
|
||||
if ( ! $query->is_single() ) {
|
||||
if ( $paged > 1 ) {
|
||||
$request = remove_query_arg( 'page' );
|
||||
$request = add_query_arg( 'page', $paged - 1, $request );
|
||||
$this->link_header( 'prev', $request );
|
||||
}
|
||||
|
||||
if ( $nextpage <= $max_page ) {
|
||||
$request = remove_query_arg( 'page' );
|
||||
$request = add_query_arg( 'page', $nextpage, $request );
|
||||
$this->link_header( 'next', $request );
|
||||
}
|
||||
}
|
||||
|
||||
$this->header( 'X-WP-Total', $query->found_posts );
|
||||
$this->header( 'X-WP-TotalPages', $max_page );
|
||||
|
||||
do_action('woocommerce_api_query_navigation_headers', $this, $query);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve the raw request entity (body)
|
||||
*
|
||||
* @since 2.1
|
||||
* @return string
|
||||
*/
|
||||
public function get_raw_data() {
|
||||
global $HTTP_RAW_POST_DATA;
|
||||
|
||||
// A bug in PHP < 5.2.2 makes $HTTP_RAW_POST_DATA not set by default,
|
||||
// but we can do it ourself.
|
||||
if ( !isset( $HTTP_RAW_POST_DATA ) ) {
|
||||
$HTTP_RAW_POST_DATA = file_get_contents( 'php://input' );
|
||||
}
|
||||
|
||||
return $HTTP_RAW_POST_DATA;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an RFC3339 timestamp into a DateTime
|
||||
*
|
||||
* @param string $date RFC3339 timestamp
|
||||
* @param boolean $force_utc Force UTC timezone instead of using the timestamp's TZ?
|
||||
* @return DateTime
|
||||
*/
|
||||
public function parse_date( $date, $force_utc = false ) {
|
||||
// Default timezone to the server's current one
|
||||
$timezone = self::get_timezone();
|
||||
if ( $force_utc ) {
|
||||
$date = preg_replace( '/[+-]\d+:?\d+$/', '+00:00', $date );
|
||||
$timezone = new DateTimeZone( 'UTC' );
|
||||
}
|
||||
|
||||
// Strip millisecond precision (a full stop followed by one or more digits)
|
||||
if ( strpos( $date, '.' ) !== false ) {
|
||||
$date = preg_replace( '/\.\d+/', '', $date );
|
||||
}
|
||||
$datetime = DateTime::createFromFormat( DateTime::RFC3339, $date );
|
||||
|
||||
return $datetime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a local date with its GMT equivalent, in MySQL datetime format
|
||||
*
|
||||
* @param string $date RFC3339 timestamp
|
||||
* @param boolean $force_utc Should we force UTC timestamp?
|
||||
* @return array Local and UTC datetime strings, in MySQL datetime format (Y-m-d H:i:s)
|
||||
*/
|
||||
public function get_date_with_gmt( $date, $force_utc = false ) {
|
||||
$datetime = $this->parse_date( $date, $force_utc );
|
||||
|
||||
$datetime->setTimezone( self::get_timezone() );
|
||||
$local = $datetime->format( 'Y-m-d H:i:s' );
|
||||
|
||||
$datetime->setTimezone( new DateTimeZone( 'UTC' ) );
|
||||
$utc = $datetime->format('Y-m-d H:i:s');
|
||||
|
||||
return array( $local, $utc );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the timezone object for the site
|
||||
*
|
||||
* @return DateTimeZone
|
||||
*/
|
||||
public function get_timezone() {
|
||||
static $zone = null;
|
||||
if ($zone !== null)
|
||||
return $zone;
|
||||
|
||||
$tzstring = get_option( 'timezone_string' );
|
||||
if ( ! $tzstring ) {
|
||||
// Create a UTC+- zone if no timezone string exists
|
||||
$current_offset = get_option( 'gmt_offset' );
|
||||
if ( 0 == $current_offset )
|
||||
$tzstring = 'UTC';
|
||||
elseif ($current_offset < 0)
|
||||
$tzstring = 'Etc/GMT' . $current_offset;
|
||||
else
|
||||
$tzstring = 'Etc/GMT+' . $current_offset;
|
||||
}
|
||||
$zone = new DateTimeZone( $tzstring );
|
||||
return $zone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract headers from a PHP-style $_SERVER array
|
||||
*
|
||||
* @since 2.1
|
||||
* @param array $server Associative array similar to $_SERVER
|
||||
* @return array Headers extracted from the input
|
||||
*/
|
||||
public function get_headers($server) {
|
||||
$headers = array();
|
||||
// CONTENT_* headers are not prefixed with HTTP_
|
||||
$additional = array('CONTENT_LENGTH' => true, 'CONTENT_MD5' => true, 'CONTENT_TYPE' => true);
|
||||
|
||||
foreach ($server as $key => $value) {
|
||||
if ( strpos( $key, 'HTTP_' ) === 0) {
|
||||
$headers[ substr( $key, 5 ) ] = $value;
|
||||
}
|
||||
elseif ( isset( $additional[ $key ] ) ) {
|
||||
$headers[ $key ] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current request accepts a JSON response by checking the endpoint suffix (.json) or
|
||||
* the HTTP ACCEPT header
|
||||
*
|
||||
* @since 2.1
|
||||
* @return bool
|
||||
*/
|
||||
private function is_json_request() {
|
||||
|
||||
// check path
|
||||
if ( false !== stripos( $this->path, '.json' ) )
|
||||
return true;
|
||||
|
||||
// check ACCEPT header, only 'application/json' is acceptable, see RFC 4627
|
||||
if ( isset( $this->headers['ACCEPT'] ) && 'application/json' == $this->headers['ACCEPT'] )
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current request accepts an XML response by checking the endpoint suffix (.xml) or
|
||||
* the HTTP ACCEPT header
|
||||
*
|
||||
* @since 2.1
|
||||
* @return bool
|
||||
*/
|
||||
private function is_xml_request() {
|
||||
|
||||
// check path
|
||||
if ( false !== stripos( $this->path, '.xml' ) )
|
||||
return true;
|
||||
|
||||
// check headers, 'application/xml' or 'text/xml' are acceptable, see RFC 2376
|
||||
if ( isset( $this->headers['ACCEPT'] ) && ( 'application/xml' == $this->headers['ACCEPT'] || 'text/xml' == $this->headers['ACCEPT'] ) )
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue