fix config
[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->markup = function($page) use($pld) {
81 return $this->reference->getFormatter()->markup($page, $pld);
82 };
83
84 $pld->refs = $this->reference;
85 $pld->baseUrl = $this->baseUrl;
86
87 return $pld;
88 }
89
90 /**
91 * Redirect to canonical url
92 * @param string $cnn
93 */
94 private function serveCanonical(string $cnn) : void {
95 $this->response->setHeader("Location", $this->baseUrl->mod(["path" => $cnn]));
96 $this->response->setResponseCode(301);
97 if (is_resource($this->output)) {
98 $this->response->send($this->output);
99 } else {
100 $this->response->send();
101 }
102 }
103
104 /**
105 * Serve index.css
106 */
107 private function serveStylesheet() : void {
108 $this->response->setHeader("Content-Type", "text/css");
109 $this->response->setBody(new Body(\fopen(ROOT."/public/index.css", "r")));
110 if (is_resource($this->output)) {
111 $this->response->send($this->output);
112 } else {
113 $this->response->send();
114 }
115 }
116
117 /**
118 * Serve index.js
119 */
120 private function serveJavascript() : void {
121 $this->response->setHeader("Content-Type", "application/javascript");
122 $this->response->setBody(new Body(\fopen(ROOT."/public/index.js", "r")));
123 if (is_resource($this->output)) {
124 $this->response->send($this->output);
125 } else {
126 $this->response->send();
127 }
128 }
129
130 /**
131 * Server a PHP stub
132 * @throws Exception
133 *
134 */
135 private function serveStub() : void {
136 $name = $this->request->getQuery("ref", "s");
137 $repo = $this->reference->getRepoForEntry($name);
138 if (!$repo->hasStub($stub)) {
139 throw new Exception(404, "Stub not found");
140 }
141 $this->response->setHeader("Content-Type", "application/x-php");
142 $this->response->setContentDisposition(["attachment" => ["filename" => "$name.stub.php"]]);
143 $this->response->setBody(new Body(\fopen($stub, "r")));
144 if (is_resource($this->output)) {
145 $this->response->send($this->output);
146 } else {
147 $this->response->send();
148 }
149 }
150
151 /**
152 * Serve a preset
153 * @param object $pld
154 * @return true to continue serving the payload
155 * @throws Exception
156 */
157 private function servePreset(object $pld) : bool {
158 switch ($pld->ref) {
159 case "AUTHORS":
160 case "LICENSE":
161 case "VERSION":
162 $pld->text = file_get_contents(ROOT."/$pld->ref");
163 return true;
164 case "index.css":
165 $this->serveStylesheet();
166 break;
167 case "index.js":
168 $this->serveJavascript();
169 break;
170 case "stub":
171 $this->serveStub();
172 break;
173 default:
174 throw new Exception(404, "$pld->ref not found");
175 }
176 return false;
177 }
178
179 /**
180 * Serve a payload
181 */
182 private function serve() : void {
183 extract((array) func_get_arg(0));
184 include ROOT."/views/layout.phtml";
185 $this->response->addHeader("Link", "<" . $this->baseUrl->path . "index.css>; rel=preload; as=style");
186 $this->response->addHeader("Link", "<" . $this->baseUrl->path . "index.js>; rel=preload; as=script");
187 if (isset($exception) && $exception->getCode()) {
188 $this->response->setResponseCode($exception->getCode());
189 }
190 if (is_resource($this->output)) {
191 $this->response->send($this->output);
192 } else {
193 $this->response->send();
194 }
195 }
196
197 /**
198 * Request handler
199 */
200 public function handle() : void {
201 try {
202 $pld = $this->createPayload();
203
204 if (isset($pld->ref) && strlen($pld->ref)) {
205 $cnn = null;
206 if (($repo = $this->reference->getRepoForEntry($pld->ref, $cnn))) {
207 if (isset($cnn) && strlen($cnn)) {
208 /* redirect */
209 $this->serveCanonical($cnn);
210 return;
211 } else {
212 /* direct match */
213 $pld->entry = $repo->getEntry($pld->ref);
214 }
215 } elseif (!$this->servePreset($pld)) {
216 return;
217 }
218 }
219
220 } catch (\Exception $e) {
221 $pld->exception = $e;
222 }
223
224 $this->serve($pld);
225 }
226 }