Lines 89.85% 62 / 69
Methods 66.66% 8 / 12
Classes 0.00% 0 / 1
Name Lines Methods CRAP
 withHtml 86.66% 13 / 15 0.00% 0 / 1 5.06
 html 100.00% 2 / 2 100.00% 1 / 1 1
 withXml 100.00% 6 / 6 100.00% 1 / 1 1
 withJson 100.00% 7 / 7 100.00% 1 / 1 2
 json 100.00% 2 / 2 100.00% 1 / 1 1
 xml 100.00% 2 / 2 100.00% 1 / 1 1
 withLastModified 100.00% 1 / 1 100.00% 1 / 1 1
 lastModified 100.00% 2 / 2 100.00% 1 / 1 1
 getCachableResponse 78.57% 11 / 14 0.00% 0 / 1 5.25
 redirect 100.00% 11 / 11 100.00% 1 / 1 4
 requireContainer 75.00% 3 / 4 0.00% 0 / 1 2.06
 requireResponse 66.66% 2 / 3 0.00% 0 / 1 2.15
16class Render
17{
18    public static ?ContainerInterface $container = null;
19
20    public static ?RequestInterface $request = null;
21
22    public static ?ResponseInterface $response = null;
23
24    public static function withHtml(
25        ResponseInterface $response,
26        string $template,
27        array $parameters = [],
28        int $status = 200
29    ): ResponseInterface {
30        Profiler::add("Controller");
31        $response  = $response->withStatus($status);
32        $response  = $response->withHeader('Content-Type', 'text/html; charset=utf-8');
33        App::$templatedefaults['debug'] = App::DEBUG;
34        $request = self::$request;
35        if (null === $request && null !== self::$container) {
36            $request = self::$container->get('request');
37            $request = $request instanceof RequestInterface ? $request : null;
38        }
39        $templateRequest = $request instanceof Request ? $request : null;
40        App::$templatedefaults['includeUrl'] = Helper\TemplateUrls::resolveIncludeUrl($templateRequest);
41        App::$templatedefaults['baseUrl'] = Helper\TemplateUrls::resolveBaseUrl($templateRequest);
42        $parameters = array_merge(App::$templatedefaults, $parameters);
43        $response  = self::requireContainer()->get('view')->render($response, $template, $parameters);
44        Profiler::add("Rendering");
45        return $response ;
46    }
47
48    public static function html(string $template, array $parameters = [], int $status = 200): ResponseInterface
49    {
50        self::$response = self::withHtml(self::requireResponse(), $template, $parameters, $status);
51        return self::$response;
52    }
53
54    public static function withXml(ResponseInterface $response, string $data, int $status = 200): ResponseInterface
55    {
56        Profiler::add("Controller");
57        $response = $response->withStatus($status);
58        $response = $response->withHeader('Content-Type', 'application/soap+xml');
59        $response->getBody()->write($data);
60        Profiler::add("Rendering");
61        return $response;
62    }
63
64    public static function withJson(ResponseInterface $response, mixed $data, int $status = 200): ResponseInterface
65    {
66        Profiler::add("Controller");
67        $response = $response->withStatus($status);
68        $response = $response->withHeader('Content-Type', 'application/json');
69        $payload = json_encode($data, JSON_UNESCAPED_SLASHES);
70        $response->getBody()->write($payload !== false ? $payload : 'null');
71        Profiler::add("Rendering");
72        return $response;
73    }
74
75    public static function json(mixed $data, int $status = 200): ResponseInterface
76    {
77        self::$response = self::withJson(self::requireResponse(), $data, $status);
78        return self::$response;
79    }
80
81    public static function xml(string $data, int $status = 200): ResponseInterface
82    {
83        self::$response = self::withXml(self::requireResponse(), $data, $status);
84        return self::$response;
85    }
86
87    /**
88     * Add `Last-Modified` header to PSR7 response object
89     *
90     * @param ResponseInterface $response A PSR7 response object
91     * @param int|string        $date     A UNIX timestamp or a valid `strtotime()` string
92     * @param string $expires
93     *
94     * @return ResponseInterface           A new PSR7 response object with `Last-Modified` header
95     *
96     */
97    public static function withLastModified(
98        ResponseInterface $response,
99        string|int $date,
100        string $expires = '+5 minutes'
101    ): ResponseInterface {
102        return self::getCachableResponse($response, $date, $expires);
103    }
104
105    /**
106     * @param string|int $date strtotime interpreted
107     * @param string $expires strtotime interpreted
108     *
109     * @return ResponseInterface
110     */
111    public static function lastModified(string|int $date, string $expires = '+5 minutes'): ResponseInterface
112    {
113        self::$response = self::withLastModified(self::requireResponse(), $date, $expires);
114        return self::$response;
115    }
116
117    /**
118     * @param string|int $date strtotime interpreted
119     * @param string $expires strtotime interpreted
120     *
121     * @return ResponseInterface
122     */
123    public static function getCachableResponse(
124        ResponseInterface $response,
125        string|int $date,
126        string $expires = '+5 minutes'
127    ): ResponseInterface {
128        if (is_int($date)) {
129            $timestamp = $date !== 0 ? $date : time();
130        } else {
131            $parsed = strtotime($date);
132            $timestamp = $parsed !== false ? $parsed : time();
133        }
134
135        $expireTs = strtotime($expires);
136        if ($expireTs === false) {
137            $expires = '+' . $expires . ' seconds';
138            $maxAge = (int) $expires;
139        } else {
140            $maxAge = $expireTs - time();
141        }
142        $response = $response->withAddedHeader('Cache-Control', 'max-age=' . $maxAge);
143        $cache = self::requireContainer()->get('cache');
144        $response = $cache->withExpires($response, $expires);
145        $response = $cache->withLastModified($response, $timestamp);
146
147        return $response;
148    }
149
150    /**
151     * @param string $route_name
152     * @param array $arguments parameters in the route path
153     * @param array|null $parameter parameters to append with "?"
154     * @param int $statuscode see an HTTP reference
155     */
156    public static function redirect(
157        string $route_name,
158        array $arguments,
159        ?array $parameter = null,
160        int $statuscode = 302
161    ): Response {
162        Profiler::add("Controller");
163
164        $url = App::$slim->urlFor($route_name, $arguments);
165        $sanitized = Helper::proxySanitizeUri($url);
166        $url = is_string($sanitized) ? $sanitized : $url;
167        $url = preg_replace('#^.*?(https?://)#', '\1', $url) ?? $url; // allow http:// routes
168        if ($parameter !== null && $parameter !== []) {
169            $url .= '?' . http_build_query($parameter);
170        }
171
172        $response = App::$slim->getResponseFactory()->createResponse($statuscode);
173        $response = self::requireContainer()->get('cache')->denyCache($response);
174        /** @var Response $response */
175        $response = $response->withHeader('Location', $url);
176
177        return $response->withAddedHeader('Cache-Control', 'max-age=0');
178    }
179
180    private static function requireContainer(): ContainerInterface
181    {
182        $container = App::$slim->getContainer();
183        if (!$container instanceof ContainerInterface) {
184            throw new \RuntimeException('Slim container is not initialized');
185        }
186        return $container;
187    }
188
189    private static function requireResponse(): ResponseInterface
190    {
191        if (!self::$response instanceof ResponseInterface) {
192            throw new \RuntimeException('Render response is not initialized');
193        }
194        return self::$response;
195    }
196}