Source Code: SmartFactory/JsonRequestHandler.php

  1: <?php
  2: /**
  3:  * This file contains the implementation of the interface IRequestHandler
  4:  * in the class JsonRequestHandler for handling the JSON API requests.
  5:  *
  6:  * @package System
  7:  *
  8:  * @author Oleg Schildt
  9:  */
 10: 
 11: namespace SmartFactory;
 12: 
 13: use \SmartFactory\Interfaces\IErrorHandler;
 14: 
 15: /**
 16:  * Class for handling the JSON API requests.
 17:  *
 18:  * The user should derive from this class and implement the methods with the names
 19:  * of the API actions.
 20:  *
 21:  * URL: http://smartfactorydev.loc/api/login/
 22:  *
 23:  * Action: login
 24:  *
 25:  * ```php
 26:  * function login()
 27:  * {
 28:  *     if (empty($this->request_data["login"]) || empty($this->request_data["password"]) ||
 29:  *         $this->request_data["login"] != "admin" || $this->request_data["password"] != "qwerty") {
 30:  *         throw new SmartException("Wrong login or password!", "wrong_login");
 31:  *     }
 32:  *
 33:  *     $this->response_data["welcome_msg"] = "Welcome, admin!";
 34:  * }
 35:  * ```
 36:  *
 37:  * @author Oleg Schildt
 38:  */
 39: abstract class JsonRequestHandler extends RequestHandler
 40: {
 41:     /**
 42:      * Internal variable for storing the request data.
 43:      *
 44:      * @var array
 45:      *
 46:      * @author Oleg Schildt
 47:      */
 48:     protected array $request_data = [];
 49: 
 50:     /**
 51:      * Internal variable for storing the response data.
 52:      *
 53:      * @var array
 54:      *
 55:      * @author Oleg Schildt
 56:      */
 57:     protected array $response_data = [];
 58: 
 59:     /**
 60:      * This is an auxiliary function for sending the response in JSON format.
 61:      *
 62:      * It sends the collected response data and the response headers. The header
 63:      * "Content-type: application/json; charset=UTF-8" ist sent automatically.
 64:      *
 65:      * @return void
 66:      *
 67:      * @throws SmartException
 68:      * It might throw exceptions in the case of any errors.
 69:      *
 70:      * @author Oleg Schildt
 71:      */
 72:     protected function sendResponse(): void
 73:     {
 74:         $this->addResponseHeader("Content-type", "application/json; charset=UTF-8");
 75: 
 76:         if (!empty($this->response_headers)) {
 77:             if (is_array($this->response_headers)) {
 78:                 foreach ($this->response_headers as $header => $value) {
 79:                     header($header . ": " . $value);
 80:                 }
 81:             }
 82:         }
 83: 
 84:         $this->addMessagesToResponse();
 85: 
 86:         if (empty($this->response_data)) {
 87:             echo json_encode($this->response_data, JSON_FORCE_OBJECT | JSON_PRETTY_PRINT);
 88:         } else {
 89:             echo json_encode($this->response_data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
 90:         }
 91:     } // sendResponse
 92: 
 93:     /**
 94:      * This is an auxiliary function for parsing the incoming JSON data.
 95:      *
 96:      * It validates the content type 'application/json' and takes the data from RAWDATA.
 97:      *
 98:      * @return void
 99:      *
100:      * @throws \Exception|SmartException
101:      * It might throw an exception if the content type oк JSON data is invalid.
102:      *
103:      * @author Oleg Schildt
104:      */
105:     protected function parseInput(): void
106:     {
107:         try {
108:             $content_type = get_header("Content-Type");
109:             
110:             if (empty($content_type)) {
111:                 throw new SmartException("Content type header 'Content-Type' is missing, expected 'application/json; charset=UTF-8'!", SmartException::ERR_CODE_INVALID_CONTENT_TYPE);
112:             }
113: 
114:             if (!preg_match("/application\/json.*charset=UTF-8/i", $content_type)) {
115:                 throw new SmartException(sprintf("Content type 'application/json; charset=UTF-8' is expected, got '%s'!", $content_type), SmartException::ERR_CODE_INVALID_CONTENT_TYPE);
116:             }
117: 
118:             $json = trim(file_get_contents("php://input"));
119: 
120:             if (empty($json)) {
121:                 throw new SmartException("The request JSON is empty!", SmartException::ERR_CODE_MISSING_REQUEST_DATA);
122:             }
123: 
124:             $json = json_decode($json, true);
125: 
126:             if (empty($json) && !is_array($json)) {
127:                 throw new SmartException(json_last_error_msg(), SmartException::ERR_CODE_JSON_PARSE_ERROR);
128:             }
129: 
130:             $this->request_data = array_merge($this->request_data, $json);
131:         } catch (SmartException $ex) {
132:             throw $ex;
133:         } catch (\Throwable $ex) {
134:             singleton(IErrorHandler::class)->handleException($ex, E_USER_ERROR);
135: 
136:             throw new SmartException($ex->getMessage(), SmartException::ERR_CODE_JSON_PARSE_ERROR);
137:         }
138:     } // parseInput
139: } // JsonRequestHandler