stub2ref
[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 (isset($exception) && $exception->getCode()) {
184 $this->response->setResponseCode($exception->getCode());
185 }
186 if (is_resource($this->output)) {
187 $this->response->send($this->output);
188 } else {
189 $this->response->send();
190 }
191 }
192
193 /**
194 * Request handler
195 */
196 public function handle() : void {
197 try {
198 $pld = $this->createPayload();
199
200 if (isset($pld->ref) && strlen($pld->ref)) {
201 $cnn = null;
202 if (($repo = $this->reference->getRepoForEntry($pld->ref, $cnn))) {
203 if (isset($cnn) && strlen($cnn)) {
204 /* redirect */
205 $this->serveCanonical($cnn);
206 return;
207 } else {
208 /* direct match */
209 $pld->entry = $repo->getEntry($pld->ref);
210 }
211 } elseif (!$this->servePreset($pld)) {
212 return;
213 }
214 }
215
216 } catch (\Exception $e) {
217 $pld->exception = $e;
218 }
219
220 $this->serve($pld);
221 }
222 }