Source Code: SmartFactory/XmlRequestHandler.php

  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