Passed
Push — master ( af5bad...67bde4 )
by Alexander
02:36
created

Page::part()   A

Complexity

Conditions 4
Paths 9

Size

Total Lines 27
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 19
c 1
b 0
f 0
nc 9
nop 1
dl 0
loc 27
rs 9.6333
1
<?php
2
3
namespace alkemann\h2l\response;
4
5
use alkemann\h2l\Environment;
6
use alkemann\h2l\exceptions\ConfigMissing;
7
use alkemann\h2l\exceptions\InvalidUrl;
8
use alkemann\h2l\Log;
9
use alkemann\h2l\Message;
10
use alkemann\h2l\Request;
11
use alkemann\h2l\Response;
12
use alkemann\h2l\util\Http;
13
14
/**
15
 * Class Page
16
 *
17
 * @package alkemann\h2l
18
 */
19
class Page extends Response
20
{
21
    /**
22
     * Overwrite this in view templates to set layout, i.e. `$this->layout = 'slim';`
23
     *
24
     * @var string
25
     */
26
    public $layout = 'default';
27
    /**
28
     * @var null|Request
29
     */
30
    protected $request = null;
31
    /**
32
     * @var array
33
     */
34
    protected $data = [];
35
    /**
36
     * @var string
37
     */
38
    private $template = 'error.html';
39
    /**
40
     * @var string
41
     */
42
    private $content_type = Http::CONTENT_HTML;
43
    /**
44
     * @var int
45
     */
46
    private $code = 200;
47
    /**
48
     * @var array
49
     */
50
    protected $config = [];
51
52
    /**
53
     * Constructor
54
     *
55
     * @param array $data
56
     * @param array $config
57
     */
58
    public function __construct($data = [], array $config = [])
59
    {
60
        $this->data = $data;
61
        foreach (['request', 'content_type', 'code', 'layout'] as $key) {
62
            if (isset($config[$key])) {
63
                $this->{$key} = $config[$key];
64
                unset($config[$key]);
65
            }
66
        }
67
        if (isset($config['template'])) {
68
            $this->setTemplate($config['template']);
69
        }
70
        $this->config = $config;
71
72
        $this->message = (new Message())
73
            ->withCode($this->code)
74
            ->withHeader('Content-Type', $this->content_type)
75
        ;
76
    }
77
78
    /**
79
     * Analyze the request url to convert to a view template
80
     *
81
     * @param Request $request
82
     * @param array $config
83
     * @return Page
84
     */
85
    public static function fromRequest(Request $request, array $config = []): Page
86
    {
87
        $config += [
88
            'request' => $request,
89
            'content_type' => $request->acceptType(),
90
        ];
91
        $page = new static($request->pageVars(), $config);
92
        $route = $request->route();
93
        $url = $route ? $route->url() : $request->url();
94
        $page->template = $config['template'] ?? $page->templateFromUrl($url);
95
        return $page;
96
    }
97
98
    /**
99
     * Returns true if a file can be found that matches the "template" that is set
100
     *
101
     * @return bool
102
     * @throws InvalidUrl is thrown if the file is missing
103
     */
104
    public function isValid(): bool
105
    {
106
        $file = $this->getContentFile($this->template);
107
        if (!file_exists($file)) {
108
            throw new InvalidUrl("Missing view file: [ $file ]");
109
        }
110
        return true;
111
    }
112
113
    /**
114
     * Provide data (variables) that are to be extracted into the view (and layout) templates
115
     *
116
     * @param string|array $key an array of data or the name for $value
117
     * @param mixed $value if $key is a string, this can be the value of that var
118
     */
119
    public function setData($key, $value = null): void
120
    {
121
        if (is_array($key)) {
122
            foreach ($key as $k => $v) {
123
                $this->data[$k] = $v;
124
            }
125
        } else {
126
            $this->data[$key] = $value;
127
        }
128
    }
129
130
    /**
131
     * Returns the `Request` of this response
132
     *
133
     * @return null|Request
134
     */
135
    public function request(): ?Request
136
    {
137
        return $this->request;
138
    }
139
140
    /**
141
     * @TODO refactor, and cache
142
     * @return string
143
     */
144
    private function head(): string
145
    {
146
        $headfile = $this->getLayoutFile('head');
147
        $neckfile = $this->getLayoutFile('neck');
148
        if (!$headfile && !$neckfile) {
149
            return '';
150
        }
151
        ob_start();
152
        try {
153
            if ($headfile && file_exists($headfile)) {
154
                $sldkfjlksejflskjflskdjflskdfj = $headfile;
155
                (function () use ($sldkfjlksejflskjflskdjflskdfj) {
156
                    extract($this->data);
157
                    include $sldkfjlksejflskjflskdjflskdfj;
158
                })();
159
            }
160
161
            if ($neckfile && file_exists($neckfile)) {
162
                $lidsinqjhsdfytqkwjkasjdksadsdg = $neckfile;
163
                (function () use ($lidsinqjhsdfytqkwjkasjdksadsdg) {
164
                    extract($this->data);
165
                    include $lidsinqjhsdfytqkwjkasjdksadsdg;
166
                })();
167
            }
168
        } finally {
169
            $ret = ob_get_contents();
170
            ob_end_clean();
171
        }
172
        return is_string($ret) ? $ret : '';
1 ignored issue
show
introduced by
The condition is_string($ret) is always true.
Loading history...
173
    }
174
175
    /**
176
     * @param string $name
177
     * @return string|null
178
     */
179
    private function getLayoutFile(string $name): ?string
180
    {
181
        $path = $this->config['layout_path'] ?? Environment::get('layout_path');
182
        if (is_null($path)) {
183
            return null;
184
        }
185
        $ending = Http::fileEndingFromType($this->content_type);
186
        return $path . $this->layout . DIRECTORY_SEPARATOR . $name . '.' . $ending . '.php';
187
    }
188
189
    /**
190
     * @param string $view
191
     * @return string
192
     * @throws ConfigMissing if neither content nor template paths are found
193
     */
194
    private function getContentFile(string $view): string
195
    {
196
        $path = $this->config['content_path']
197
            ?? $this->config['template_path']
198
            ?? Environment::get('content_path')
199
            ?? Environment::get('template_path');
200
201
        if (is_null($path)) {
202
            throw new ConfigMissing("No `content_path` or `template_path` configured!");
203
        }
204
        return $path . $view . '.php';
205
    }
206
207
    /**
208
     * @TODO refactor, and cache
209
     * @return string
210
     */
211
    private function foot(): string
212
    {
213
        $footfile = $this->getLayoutFile('foot');
214
        if ($footfile && file_exists($footfile)) {
215
            ob_start();
216
            try {
217
                $ldkfoskdfosjicyvutwehkshfskjdf = $footfile;
218
                (function () use ($ldkfoskdfosjicyvutwehkshfskjdf) {
219
                    extract($this->data);
220
                    include $ldkfoskdfosjicyvutwehkshfskjdf;
221
                })();
222
            } finally {
223
                $ret = ob_get_contents();
224
                ob_end_clean();
225
            }
226
            return is_string($ret) ? $ret : '';
1 ignored issue
show
introduced by
The condition is_string($ret) is always true.
Loading history...
227
        } else {
228
            return '';
229
        }
230
    }
231
232
    /**
233
     * @TODO refactor, and cache
234
     * @TODO BUG, method should be private
235
     * @param string $view
236
     * @return string
237
     * @throws InvalidUrl if no view file found
238
     */
239
    public function view(string $view): string
240
    {
241
        $file = $this->getContentFile($view);
242
        ob_start();
243
        try {
244
            // or another way to hide the file variable?
245
            $dsfjskdfjsdlkfjsdkfjsdkfjsdlkfjsd = $file;
246
            (function () use ($dsfjskdfjsdlkfjsdkfjsdkfjsdlkfjsd) {
247
                extract($this->data);
248
                include $dsfjskdfjsdlkfjsdkfjsdkfjsdlkfjsd;
249
            })();
250
        } finally {
251
            $ret = ob_get_contents();
252
            ob_end_clean();
253
        }
254
        return is_string($ret) ? $ret : '';
1 ignored issue
show
introduced by
The condition is_string($ret) is always true.
Loading history...
255
    }
256
257
    /**
258
     * Render a parts file to include in view for reusing parts
259
     *
260
     * @param string $name name of part file to include, doesnt include path or file ending
261
     * @return string
262
     * @throws ConfigMissing if `parts_path` is not configured
263
     */
264
    public function part(string $name): string
265
    {
266
        $parts_path = Environment::get('parts_path');
267
        if (!$parts_path) {
268
            $content_path = Environment::get('content_path');
269
            if ($content_path) {
270
                $parts_path = $content_path . ".." . DIRECTORY_SEPARATOR . "parts" . DIRECTORY_SEPARATOR;
271
            } else {
272
                throw new ConfigMissing("Missing `parts_path` (or `content_path`) configuration!");
273
            }
274
        }
275
        $ending = Http::fileEndingFromType($this->content_type);
276
277
        $parts_file = $parts_path . "{$name}.{$ending}.php";
278
279
        ob_start();
280
        try {
281
            $popsemdsdfosjicyvsoaowkdawd = $parts_file;
282
            (function () use ($popsemdsdfosjicyvsoaowkdawd) {
283
                extract($this->data);
284
                include $popsemdsdfosjicyvsoaowkdawd;
285
            })();
286
        } finally {
287
            $ret = ob_get_contents();
288
            ob_end_clean();
289
        }
290
        return is_string($ret) ? $ret : '';
1 ignored issue
show
introduced by
The condition is_string($ret) is always true.
Loading history...
291
    }
292
293
    /**
294
     * Set header type, render the view, then optionally render layouts and wrap the template
295
     *
296
     * @return string fully rendered string, ready to be echo'ed
297
     * @throws InvalidUrl if the view template does not exist
298
     */
299
    public function render(): string
300
    {
301
        $this->setHeaders();
302
        $view = $this->view($this->template);
303
        $response = $this->head();
304
        $response .= $view;
305
        $response .= $this->foot();
306
        $this->message = $this->message->withBody($response);
307
        return $this->message->body();
308
    }
309
310
    /**
311
     * Creates a filename from template and content type
312
     *
313
     * @param string $template
314
     */
315
    public function setTemplate(string $template): void
316
    {
317
        $ending = Http::fileEndingFromType($this->content_type);
318
        $this->template = "{$template}.{$ending}";
319
    }
320
321
    /**
322
     * @param string $url
323
     * @return string
324
     */
325
    private function templateFromUrl(string $url): string
326
    {
327
        $parts = explode('/', $url);
328
        $last = array_slice($parts, -1, 1, true);
329
        unset($parts[key($last)]);
330
        $view = current($last);
331
        $period = strrpos($view, '.');
332
        if ($period) {
333
            $ending = substr($view, $period + 1);
334
            if ($type = Http::contentTypeFromFileEnding($ending)) {
335
                $this->setContentType($type);
336
                $view = substr($view, 0, $period);
337
            }
338
        }
339
        $ending = Http::fileEndingFromType($this->content_type);
340
        $ret = join(DIRECTORY_SEPARATOR, $parts) . DIRECTORY_SEPARATOR . $view . '.' . $ending;
341
        return trim($ret, DIRECTORY_SEPARATOR);
342
    }
343
344
    /**
345
     * @param string $type
346
     */
347
    private function setContentType(string $type): void
348
    {
349
        $this->content_type = $type;
350
        $this->message = $this->message->withHeader('Content-Type', $this->content_type);
351
    }
352
}
353