-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathModelViewController.php
More file actions
191 lines (176 loc) · 6.12 KB
/
ModelViewController.php
File metadata and controls
191 lines (176 loc) · 6.12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
<?php
/*
* Copyright (c) 2021.
* Marc Concepcion
* marcanthonyconcepcion@gmail.com
*/
$configuration = yaml_parse_file(__DIR__.'\resources\MarcPHPRESTAPIDemo.yaml');
define('ModelViewController_RESOURCE', $configuration['mvc']['resource']);
const HTTP_OK = ['code' => 200, 'message' => 'HTTP/1.1 200 OK'];
const HTTP_CREATED = ['code' => 201, 'message' => 'HTTP/1.1 201 Created'];
const HTTP_NO_CONTENT = ['code' => 204, 'message' => 'HTTP/1.1 204 No Content'];
const HTTP_BAD_REQUEST = ['code' => 400, 'message' => 'HTTP/1.1 400 Bad Request'];
const HTTP_NOT_FOUND = ['code' => 404, 'message' => 'HTTP/1.1 404 Not Found'];
const HTTP_NOT_ALLOWED = ['code' => 405, 'message' => 'HTTP/1.1 405 Method Not Allowed'];
const HTTP_CONFLICT_ERROR = ['code' => 409, 'message' => 'HTTP/1.1 409 Conflict Error'];
const HTTP_INTERNAL_SERVER_ERROR = ['code' => 500, 'message' => 'HTTP/1.1 500 Internal Server Error'];
interface Model
{
function create(StdClass $model);
function retrieve(int $id);
function list();
function update(int $id, StdClass $model);
function delete(int $id);
function checkExistence(int $id);
}
class View
{
/**
* @throws HTTPBadRequestError
*/
static function receiveRequest(): object
{
header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Methods: OPTIONS,GET,POST,PUT,DELETE");
header("Access-Control-Max-Age: 3600");
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
$uri = explode('/', parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH));
if (key_exists(1, $uri) && strlen($uri[1]))
{
if ($uri[1] !== ModelViewController_RESOURCE)
{
throw new HTTPBadRequestError('Resource '.$uri[1].' does not exist. Please provide a valid REST API resource.');
}
}
else
{
throw new HTTPBadRequestError('No resource specified on the URL. Please provide a valid REST API resource.');
}
$id = null;
if (key_exists(2, $uri) && strlen($uri[2]))
{
$id = intval($uri[2]);
}
$request = new StdClass();
$request->http_command = $_SERVER["REQUEST_METHOD"];
if (key_exists('QUERY_STRING', $_SERVER))
{
parse_str($_SERVER['QUERY_STRING'], $parameters);
$request->json_parameters = json_encode($parameters);
}
return (object)['id'=>$id, 'request'=>$request];
}
static function sendResponse(StdClass $response)
{
header($response->status->message, true, $response->status->code);
if(key_exists('body', (array)$response))
{
echo $response->body;
}
}
static function sendErrorResponse(StdClass $http_error_status, string $message)
{
header($http_error_status->message, true, $http_error_status->code);
echo json_encode(json_decode('{"error": "'.$message.'"}'));
}
}
abstract class Controller
{
protected array $models;
/**
* @param int|null $id
* @throws HTTPNotFoundError
*/
abstract function get(?int $id = null);
abstract function post(string $json_parameters);
/**
* @param int $id
* @param string $json_parameters
* @throws HTTPNotFoundError
*/
abstract function put(int $id, string $json_parameters);
/**
* @param int $id
* @throws HTTPNotFoundError
*/
abstract function delete(int $id);
function register(Model $model, string $modelName)
{
$this->models[$modelName] = $model;
}
/**
* @param StdClass $request
* @param int|null $id
* @return object
* @throws HTTPNotAllowedError
* @throws HTTPNotFoundError
* @throws HTTPConflictError
*/
function processHTTP(StdClass $request, ?int $id = null): object
{
$this->evaluateHTTPCommand($request, $id);
return match ($request->http_command) {
'GET' => $this->get($id),
'POST' => $this->post($request->json_parameters),
'PUT' => $this->put($id, $request->json_parameters),
'DELETE' => $this->delete($id),
default => (object)['status'=>(object)HTTP_NOT_ALLOWED,
'body'=>json_encode(json_decode('{"error": "Cannot use '.$request->http_command.' command." }'))]
};
}
/**
* @param StdClass $request
* @param int|null $id
* @throws HTTPNotAllowedError
*/
private function evaluateHTTPCommand(StdClass $request, ?int $id)
{
if ( false === in_array($request->http_command, ['GET', 'POST', 'PUT', 'DELETE']) ||
$request->http_command === 'POST' && !is_null($id) ||
$request->http_command === 'PUT' && is_null($id) ||
$request->http_command === 'DELETE' && is_null($id) )
{
throw new HTTPNotAllowedError('HTTP command '.$request->http_command.' '.(is_null($id)?'without': 'with')
.' specified ID is not allowed. Please provide an acceptable HTTP command.');
}
if (($request->http_command === 'POST' || $request->http_command === 'PUT')
&& false === key_exists('json_parameters', (array)$request))
{
throw new HTTPNotAllowedError('HTTP command '.$request->http_command
.' without providing parameters is not allowed. Please provide an acceptable HTTP command.');
}
}
}
abstract class HTTPError extends Exception
{
abstract function getResponse();
}
class HTTPNotFoundError extends HTTPError
{
function getResponse() : object
{
return (object)HTTP_NOT_FOUND;
}
}
class HTTPBadRequestError extends HTTPError
{
function getResponse() : object
{
return (object)HTTP_BAD_REQUEST;
}
}
class HTTPNotAllowedError extends HTTPError
{
function getResponse() : object
{
return (object)HTTP_NOT_ALLOWED;
}
}
class HTTPConflictError extends HTTPError
{
function getResponse() : object
{
return (object)HTTP_CONFLICT_ERROR;
}
}