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: use \SmartFactory\Interfaces\IRequestHandler;
15:
16: /**
17: * Abstract class for handling the JSON and XML requests.
18: *
19: * @author Oleg Schildt
20: */
21: abstract class RequestHandler implements IRequestHandler
22: {
23: /**
24: * Internal variable for storing the API action.
25: *
26: * @var string
27: *
28: * @author Oleg Schildt
29: */
30: protected string $action = "";
31:
32: /**
33: * Internal variable for storing the API parameter string.
34: *
35: * @var string
36: *
37: * @author Oleg Schildt
38: */
39: protected string $param_string = "";
40:
41: /**
42: * Internal variable for storing the response headers.
43: *
44: * @var array
45: *
46: * @author Oleg Schildt
47: */
48: protected array $response_headers = [];
49:
50: /**
51: * This is an auxiliary function for getting request headers and parsing the API url.
52: *
53: * It put the retrieved data into the corresponding properties.
54: *
55: * @return void
56: *
57: * @author Oleg Schildt
58: */
59: protected function processInputData(): void
60: {
61: if (empty($_SERVER['REQUEST_URI'])) {
62: return;
63: }
64:
65: $api_base = str_replace(basename($_SERVER['SCRIPT_NAME']), "", $_SERVER['SCRIPT_NAME']);
66: $api_request = str_replace($api_base, "", $_SERVER['REQUEST_URI']);
67:
68: if (preg_match("~^([^/.?&]+)([/?]?.*)~", $api_request, $matches)) {
69: $this->action = $matches[1];
70: $this->param_string = urldecode($matches[2]);
71: } else {
72: $this->action = $_REQUEST["action"] ?? "";
73: $this->param_string = urldecode(str_replace(basename($_SERVER['SCRIPT_NAME']), "", $api_request));
74: }
75: } // processInputData
76:
77: /**
78: * This is an auxiliary function for sending the response.
79: *
80: * @return void
81: *
82: * @author Oleg Schildt
83: */
84: abstract protected function sendResponse(): void;
85:
86: /**
87: * Adds a response header to the response.
88: *
89: * @param string $header
90: * The name of the response header.
91: *
92: * @param string $value
93: * The value of the response header.
94: *
95: * @return void
96: *
97: * @author Oleg Schildt
98: */
99: protected function addResponseHeader(string $header, string $value): void
100: {
101: $this->response_headers[$header] = $value;
102: } // addResponseHeader
103:
104: /**
105: * This is an auxiliary function for parsing the incoming data.
106: *
107: * @return void
108: *
109: * @throws SmartException
110: * It might throw an exception if the content type or data is invalid.
111: *
112: * @author Oleg Schildt
113: */
114: abstract protected function parseInput(): void;
115:
116: /**
117: * This is an auxiliary function for adding error messages, warnings, info messages to the response which might have been collected
118: * during handling of the request.
119: *
120: * @return void
121: *
122: * @throws SmartException
123: * It might throw an exception if the content type or data is invalid.
124: *
125: * @author Oleg Schildt
126: */
127: abstract protected function addMessagesToResponse(): void;
128:
129: /**
130: * This function is called before the processing of the request. It should be overridden if you want
131: * to perform some standard action before every API request of your library.
132: * The usage example are - validation of the access tokens, checking maintenance mode and reporting it to
133: * the user if it is active.
134: *
135: * @return void
136: *
137: * @author Oleg Schildt
138: */
139: protected function preprocessRequest(): void
140: {
141: } // preprocessRequest
142:
143: /**
144: * This function is called after the processing of the request. It should be overridden if you want
145: * to perform some standard action after every API request of your library.
146: *
147: * @return void
148: *
149: * @author Oleg Schildt
150: */
151: protected function postprocessRequest(): void
152: {
153: } // postprocessRequest
154:
155: /**
156: * This is the main function that should be called to handle the API request.
157: *
158: * It detects the action, retrieves the request headers, parses the incoming JSON data and
159: * tries to call the corresponding method for the API action.
160: *
161: * @return void
162: *
163: * @throws \Exception
164: * It might throw exceptions in the case of any errors.
165: *
166: * @author Oleg Schildt
167: */
168: public function handleRequest(): void
169: {
170: try {
171: $this->processInputData();
172:
173: $this->parseInput();
174:
175: if (empty($this->action)) {
176: throw new SmartException("The action of the API request cannot be defined!", SmartException::ERR_CODE_SYSTEM_ERROR);
177: }
178:
179: $robject = new \ReflectionObject($this);
180: if (!$robject->hasMethod($this->action)) {
181: throw new SmartException(sprintf("No handler is defined for the API request action '%s'!", $this->action), SmartException::ERR_CODE_SYSTEM_ERROR);
182: }
183:
184: $rmethod = $robject->getMethod($this->action);
185:
186: if ($this->action == "handleRequest" || $rmethod->isConstructor() || $rmethod->isDestructor() || !$rmethod->isPublic()) {
187: throw new SmartException(sprintf("The name '%s' for the API action is not supported!", $this->action), SmartException::ERR_CODE_SYSTEM_ERROR);
188: }
189:
190: $this->preprocessRequest();
191: $rmethod->invoke($this);
192: $this->postprocessRequest();
193: } catch (SmartException $ex) {
194: messenger()->addError($ex->getMessage(), $ex->getErrorDetails(), $ex->getErrorElement(), $ex->getErrorCode(), $ex->getTechnicalInfo(), $ex->getFile(), $ex->getLine());
195: } catch (\Throwable $ex) {
196: messenger()->addError("System error occurred!", [], "", SmartException::ERR_CODE_SYSTEM_ERROR, $ex->getMessage(), $ex->getFile(), $ex->getLine());
197: singleton(IErrorHandler::class)->handleException($ex, E_USER_ERROR);
198: }
199:
200: $this->sendResponse();
201: } // handleRequest
202: } // RequestHandler