<?php
/**
 * HSDN JSON API Library
 *
 * @version     1.1.7
 *
 * @author      HSDN Team
 * @copyright   Copyright (c) 2016-2017, Information Networks Ltd.
 * @link        http://www.hsdn.org
 */

// ------------------------------------------------------------------------

class JSON_API {

    
/*
     * Массив параметров конфигурации
     *
     * @access  protected
     */
    
protected $config = array
    (
        
// Разрешить вывод массива данных запроса в ответе
        
'render_request' => FALSE,

        
// Кдировать Unicode-символы (php 5.4+)
        
'encode_unicode' => FALSE,

        
// Выводить JSON в удобно читаемом формате (php 5.4+)
        
'pretty_output'  => TRUE,

        
// Отображать дробные нули для чисел с плавающей точкой (php 5.6+)
        
'preserve_zero'  => FALSE,

        
// Разрешенные способы получения данных запроса
        
'allow_request_types' => array
        (
            
// Параметры из X-заголовков запроса
            //'HEADERS',

            // Параметры из POST-запроса
            
'POST',

            
// Параметры из GET-запроса
            
'GET',

            
// Параметры в JSON из тела POST-запроса
            
'JSON',
        ),
    );

    
/*
     * Массив данных ответа
     *
     * @access  protected
     */
    
protected $response = array();

    
/*
     * Массив данных запроса
     *
     * @access  protected
     */
    
protected $request = array();

    
/*
     * Массив данных ошибка
     *
     * @access  protected
     */
    
protected $error = array
    (
        
'status' => FALSE,
    );


    
/**
     * Создание и возврат нового объекта класса JSON_API
     *
     * @access  public
     * @param   bool
     * @return  object
     */
    
public static function factory($config NULL)
    {
        return new 
JSON_API($config);
    }

    
/**
     * Конструктор, чтение запроса
     *
     * @access  public
     * @param   bool
     * @return  void
     */
    
public function __construct($config NULL)
    {
        if (!
is_null($config))
        {
            
$this->config array_merge($this->config$config);
        }

        
$allow_types $this->config['allow_request_types'];

        if (
in_array('HEADERS'$allow_types))
        {
            
$headers array_change_key_case(getallheaders(), CASE_LOWER);

            foreach (
$headers as $key => $value)
            {
                if (
substr($key02) == 'x-')
                {
                    
$this->request[$key] = $value;
                }
            }
        }

        if (
in_array('GET'$allow_types) AND isset($_GET) AND !empty($_GET))
        {
            
$this->request array_merge($_GET$this->request);
        }

        if (isset(
$_POST))
        {
            if (
in_array('POST'$allow_types) AND !empty($_POST))
            {
                
$this->request array_merge($_POST$this->request);
            }
            elseif (
in_array('JSON'$allow_types))
            {
                
$input_data = @file_get_contents('php://input');

                if (empty(
$input_data) AND isset($GLOBALS['HTTP_RAW_POST_DATA']))
                {
                    
$input_data $GLOBALS['HTTP_RAW_POST_DATA'];
                }

                if (!empty(
$input_data) AND $json_data = @json_decode($input_dataTRUE))
                {
                    
$this->request array_merge($json_data$this->request);
                }
            }
        }

        
$this->request $this->prepare_request($this->request);
    }

    
/**
     * Установить сообщение об ошибке
     *
     * @access  public
     * @param   string
     * @return  mixed
     */
    
public function get_request($param FALSE)
    {
        if (
$param === FALSE)
        {
            return 
$this->request;
        }

        if (isset(
$this->request[$param]))
        {
            return 
$this->request[$param];
        }

        return 
NULL;
    }

    
/**
     * Установить данные для запроса
     *
     * @access  public
     * @param   mixed
     * @param   mixed
     * @return  object
     */
    
public function set_request($data$value NULL)
    {
        if (
$data === FALSE)
        {
            
$this->request FALSE;
        }
        else
        {
            if (!
is_null($value))
            {
                
$data = array
                (
                    
$data => $value
                
);
            }

            if (
is_array($data))
            {
                
$this->request array_merge($this->request$data);
            }
            else
            {
                
array_push($this->request$data);
            }
        }

        return 
$this;
    }

    
/**
     * Установить данные для ответа
     *
     * @access  public
     * @param   mixed
     * @param   mixed
     * @return  object
     */
    
public function set_response($data$value NULL)
    {
        if (
$data === FALSE)
        {
            
$this->response FALSE;
        }
        else
        {
            if (!
is_null($value))
            {
                
$data = array
                (
                    
$data => $value
                
);
            }

            if (
is_array($data))
            {
                
$this->response array_merge($this->response$data);
            }
            else
            {
                
array_push($this->response$data);
            }
        }

        return 
$this;
    }

    
/**
     * Установить сообщение об ошибке
     *
     * @access  public
     * @param   string
     * @param   mixed
     * @return  object
     */
    
public function set_error($message$code NULL)
    {
        if (!
is_null($code))
        {
            
$this->error['code'] = $code;
        }

        
$this->error['status']  = TRUE;
        
$this->error['message'] = $message;

        return 
$this;
    }

    
/**
     * Вывести построенные результаты в JSON
     *
     * @access  public
     * @param   string
     * @return  object
     */
    
public function render()
    {
        if (
$this->response === FALSE)
        {
            throw new 
Exception('Method return incomplete or incorrect response.'NULL);
        }

        
$json = array();

        if (
$this->config['render_request'])
        {
            
$json['request'] = (object) $this->request;
        }

        
$json['response'] = (object) $this->response;
        
$json['error']    = (object) $this->error;

        
$callback   = isset($_GET['callback']) ? $_GET['callback'] : NULL;
        
$parameters NULL;

        if (!
$this->config['encode_unicode'])
        {
            
$parameters |= JSON_UNESCAPED_UNICODE;
        }

        if (
$this->config['pretty_output'])
        {
            
$parameters |= JSON_PRETTY_PRINT;
        }
        if (
$this->config['preserve_zero'])
        {
            
$parameters |= JSON_PRESERVE_ZERO_FRACTION;
        }

        if (
$callback AND preg_match('/^([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)$/'$callback))
        {
            
header('Content-Type: application/javascript; charset='.mb_internal_encoding());

            echo 
$callback.'('.json_encode($json$parameters).')';
        }
        else
        {
            
header('Content-Type: application/json; charset='.mb_internal_encoding());

            echo 
json_encode($json$parameters);
        }

        return 
$this;
    }

    
/**
     * Преобразовать значения в массиве запроса
     *
     * @access  public
     * @param   array
     * @return  array
     */
    
public function prepare_request($request)
    {
        
$return = array();

        foreach (
$request as $key => $value)
        {
            if (
is_array($value))
            {
                
$return[$key] = $this->prepare_request($value);
            }
            else
            {
                switch (
strtolower($value))
                {
                    case 
'true':
                        
$return[$key] = TRUE;
                        break;

                    case 
'false':
                        
$return[$key] = FALSE;
                        break;

                    case 
'null':
                        
$return[$key] = NULL;
                        break;

                    default:
                        
$return[$key] = $value;
                }
            }
        }

        return 
$return;
    }
}

/* End of file */