fix PHP-7.4 compat
[mdref/mdref] / mdref / Action.php
1 <?php
2
3 namespace mdref;
4
5 use http\Env\Request;
6 use http\Env\Response;
7 use http\Message\Body;
8 use stdClass;
9 use function file_get_contents;
10 use function htmlspecialchars;
11 use function ob_start;
12 use const ROOT;
13
14 /**
15 * Request handler
16 */
17 class Action {
18 /**
19 * The reference
20 * @var \mdref\Reference
21 */
22 private $reference;
23
24 /**
25 * @var \http\Env\Request
26 */
27 private $request;
28
29 /**
30 * @var \http\Env\Response
31 */
32 private $response;
33
34 /**
35 * @var resource
36 */
37 private $output;
38
39 /**
40 * @var \http\Url
41 */
42 private $baseUrl;
43
44 /**
45 * Initialize the reference
46 */
47 public function __construct(Reference $ref, Request $req, Response $res, BaseUrl $baseUrl, $output = null) {
48 $this->reference = $ref;
49 $this->request = $req;
50 $this->response = $res;
51 $this->baseUrl = $baseUrl;
52 $this->output = $output;
53 ob_start($res);
54 }
55
56 /**
57 * Shorthand for \htmlspecialchars()
58 * @param $txt string
59 * @return string
60 */
61 function esc(string $txt) : string {
62 return htmlspecialchars($txt);
63 }
64
65 /**
66 * Create the view payload
67 * @return \stdClass
68 */
69 private function createPayload() : object {
70 $pld = new stdClass;
71
72 $pld->esc = "htmlspecialchars";
73 $pld->anchor = [$this->reference, "formatAnchor"];
74 $pld->quick = [$this->reference, "formatString"];
75 $pld->file = [$this->reference, "formatFile"];
76
77 $pld->ref = $this->baseUrl->pathinfo(
78 $this->baseUrl->mod($this->request->getRequestUrl()));
79
80 $pld->refs = $this->reference;
81 $pld->baseUrl = $this->baseUrl;
82
83 return $pld;
84 }
85
86 /**
87 * Redirect to canonical url
88 * @param string $cnn
89 */
90 private function serveCanonical(string $cnn) : void {
91 $this->response->setHeader("Location", $this->baseUrl->mod(["path" => $cnn]));
92 $this->response->setResponseCode(301);
93 if (is_resource($this->output)) {
94 $this->response->send($this->output);
95 } else {
96 $this->response->send();
97 }
98 }
99
100 /**
101 * Serve index.css
102 */
103 private function serveStylesheet() : void {
104 $this->response->setHeader("Content-Type", "text/css");
105 $this->response->setBody(new Body(\fopen(ROOT."/public/index.css", "r")));
106 if (is_resource($this->output)) {
107 $this->response->send($this->output);
108 } else {
109 $this->response->send();
110 }
111 }
112
113 /**
114 * Serve index.js
115 */
116 private function serveJavascript() : void {
117 $this->response->setHeader("Content-Type", "application/javascript");
118 $this->response->setBody(new Body(\fopen(ROOT."/public/index.js", "r")));
119 if (is_resource($this->output)) {
120 $this->response->send($this->output);
121 } else {
122 $this->response->send();
123 }
124 }
125
126 /**
127 * Server a PHP stub
128 * @throws Exception
129 *
130 */
131 private function serveStub() : void {
132 $name = $this->request->getQuery("ref", "s");
133 $repo = $this->reference->getRepoForEntry($name);
134 if (!$repo->hasStub($stub)) {
135 throw new Exception(404, "Stub not found");
136 }
137 $this->response->setHeader("Content-Type", "application/x-php");
138 $this->response->setContentDisposition(["attachment" => ["filename" => "$name.stub.php"]]);
139 $this->response->setBody(new Body(\fopen($stub, "r")));
140 if (is_resource($this->output)) {
141 $this->response->send($this->output);
142 } else {
143 $this->response->send();
144 }
145 }
146
147 /**
148 * Serve a preset
149 * @param object $pld
150 * @return true to continue serving the payload
151 * @throws Exception
152 */
153 private function servePreset(object $pld) : bool {
154 switch ($pld->ref) {
155 case "AUTHORS":
156 case "LICENSE":
157 case "VERSION":
158 $pld->text = file_get_contents(ROOT."/$pld->ref");
159 return true;
160 case "index.css":
161 $this->serveStylesheet();
162 break;
163 case "index.js":
164 $this->serveJavascript();
165 break;
166 case "stub":
167 $this->serveStub();
168 break;
169 default:
170 throw new Exception(404, "$pld->ref not found");
171 }
172 return false;
173 }
174
175 /**
176 * Serve a payload
177 */
178 private function serve() : void {
179 extract((array) func_get_arg(0));
180 include ROOT."/views/layout.phtml";
181 $this->response->addHeader("Link", "<" . $this->baseUrl->path . "index.css>; rel=preload; as=style");
182 $this->response->addHeader("Link", "<" . $this->baseUrl->path . "index.js>; rel=preload; as=script");
183 if (is_resource($this->output)) {
184 $this->response->send($this->output);
185 } else {
186 $this->response->send();
187 }
188 }
189
190 /**
191 * Request handler
192 */
193 public function handle() : void {
194 try {
195 $pld = $this->createPayload();
196
197 if (strlen($pld->ref)) {
198 $cnn = null;
199 if (($repo = $this->reference->getRepoForEntry($pld->ref, $cnn))) {
200 if (strlen($cnn)) {
201 /* redirect */
202 $this->serveCanonical($cnn);
203 return;
204 } else {
205 /* direct match */
206 $pld->entry = $repo->getEntry($pld->ref);
207 }
208 } elseif (!$this->servePreset($pld)) {
209 return;
210 }
211 }
212
213 } catch (\Exception $e) {
214 $pld->exception = $e;
215 }
216
217 $this->serve($pld);
218 }
219 }