1: <?php
2: /**
3: * This file contains the implementation of the interface IRequestHandler
4: * in the class XmlRequestHandler for handling the XML 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 XML 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/get_rooms/
22: *
23: * Action: get_rooms
24: *
25: * ```php
26: * function get_rooms()
27: * {
28: * $xsdpath = new \DOMXPath($this->request_xmldoc);
29: *
30: * $nodes = $xsdpath->evaluate("/Request/City");
31: * if ($nodes->length == 0) {
32: * throw new SmartException("City is undefined!", "no_city");
33: * }
34: * $city = $nodes->item(0)->nodeValue;
35: *
36: * $response = $this->response_xmldoc->createElement("Response");
37: * $this->response_xmldoc->appendChild($response);
38: *
39: * ...
40: * } // get_rooms
41: * ```
42: *
43: * @author Oleg Schildt
44: */
45: abstract class XmlRequestHandler extends RequestHandler
46: {
47: /**
48: * Internal variable for storing the request XML document.
49: *
50: * @var \DOMDocument
51: *
52: * @author Oleg Schildt
53: */
54: protected \DOMDocument $request_xmldoc;
55:
56: /**
57: * Internal variable for storing the response XML document.
58: *
59: * @var \DOMDocument
60: *
61: * @author Oleg Schildt
62: */
63: protected \DOMDocument $response_xmldoc;
64:
65: /**
66: * Constructor.
67: *
68: * It initializes the XML document variables.
69: *
70: * @author Oleg Schildt
71: */
72: function __construct() {
73: $this->request_xmldoc = new \DOMDocument("1.0", "UTF-8");
74: $this->request_xmldoc->formatOutput = true;
75:
76: $this->response_xmldoc = new \DOMDocument("1.0", "UTF-8");
77: $this->response_xmldoc->formatOutput = true;
78: }
79:
80: /**
81: * This is an auxiliary function for parsing the incoming XML data.
82: *
83: * It validates the content type 'application/xml' and takes the data from RAWDATA.
84: *
85: * @throws SmartException|\Exception
86: * It might throw an exception if the content type oк XML data is invalid.
87: *
88: * @return void
89: *
90: * @author Oleg Schildt
91: */
92: protected function parseInput(): void
93: {
94: try {
95: $content_type = get_header("Content-Type");
96:
97: if (empty($content_type)) {
98: throw new SmartException("Content type header 'Content-Type' is missing, expected 'application/xml; charset=UTF-8'!", SmartException::ERR_CODE_INVALID_CONTENT_TYPE);
99: }
100:
101: if (!preg_match("/application\/xml/", $content_type)) {
102: throw new SmartException(sprintf("Content type 'application/xml; charset=UTF-8' is expected, got '%s'!", $content_type), SmartException::ERR_CODE_INVALID_CONTENT_TYPE);
103: }
104:
105: $xmldata = trim(file_get_contents("php://input"));
106:
107: if (empty($xmldata)) {
108: throw new SmartException("The request XML is empty!", SmartException::ERR_CODE_MISSING_REQUEST_DATA);
109: }
110:
111: $this->request_xmldoc = new \DOMDocument("1.0", "UTF-8");
112: $this->request_xmldoc->formatOutput = true;
113: if (!@$this->request_xmldoc->loadXML($xmldata)) {
114: throw new SmartException("Error by XML parsing!", SmartException::ERR_CODE_XML_PARSE_ERROR);
115: }
116: } catch (SmartException $ex) {
117: throw $ex;
118: } catch (\Throwable $ex) {
119: singleton(IErrorHandler::class)->handleException($ex, E_USER_ERROR);
120:
121: throw new SmartException($ex->getMessage(), SmartException::ERR_CODE_XML_PARSE_ERROR);
122: }
123: } // parseInput
124:
125: /**
126: * This is an auxiliary function for sending the response in XML format.
127: *
128: * It sends the prepared response XML document and the response headers. The header
129: * "Content-type: application/xml" ist sent automatically.
130: *
131: * @return void
132: *
133: * @throws SmartException
134: * It might throw an exception in the case of any errors.
135: *
136: * @author Oleg Schildt
137: */
138: protected function sendResponse(): void
139: {
140: $this->addResponseHeader("Content-type", "application/xml; charset=UTF-8");
141:
142: header('Content-type: application/xml');
143:
144: if (!empty($this->response_headers)) {
145: if (is_array($this->response_headers)) {
146: foreach ($this->response_headers as $header) {
147: header($header);
148: }
149: }
150: }
151:
152: $this->addMessagesToResponse();
153:
154: echo $this->response_xmldoc->saveXML();
155: } // sendResponse
156: } // XmlRequestHandler