Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
73 / 73
100.00% covered (success)
100.00%
16 / 16
CRAP
n/a
0 / 0
helpers
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
4
esc
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
view
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
current_url
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
current_route
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
route_url
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
4
lang
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
session
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
old
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
6
has_old
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
csrf_input
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
respond_not_found
100.00% covered (success)
100.00%
18 / 18
100.00% covered (success)
100.00%
1 / 1
3
redirect
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
redirect_to
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
config
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
3
model
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
1<?php declare(strict_types=1);
2/*
3 * This file is part of App Project.
4 *
5 * For the full copyright and license information, please view the LICENSE
6 * file that was distributed with this source code.
7 */
8/**
9 * @package app
10 */
11
12use Framework\Helpers\ArraySimple;
13use Framework\HTTP\Response;
14use Framework\MVC\App;
15use Framework\MVC\Model;
16use Framework\Routing\Route;
17use Framework\Session\Session;
18use JetBrains\PhpStorm\Pure;
19
20/**
21 * Load helper files.
22 *
23 * @param array<int,string>|string $helper A list of helper names as array
24 * or a helper name as string
25 *
26 * @return array<int,string> A list of all loaded files
27 */
28function helpers(array | string $helper) : array
29{
30    if (is_array($helper)) {
31        $files = [];
32        foreach ($helper as $item) {
33            $files[] = helpers($item);
34        }
35        return array_merge(...$files);
36    }
37    $files = App::locator()->findFiles('Helpers/' . $helper);
38    foreach ($files as $file) {
39        require_once $file;
40    }
41    return $files;
42}
43
44/**
45 * Escape special characters to HTML entities.
46 *
47 * @param string|null $text The text to be escaped
48 * @param string $encoding The escaped text encoding
49 *
50 * @return string The escaped text
51 */
52#[Pure]
53function esc(?string $text, string $encoding = 'UTF-8') : string
54{
55    $text = (string) $text;
56    return empty($text)
57        ? $text
58        : htmlspecialchars($text, \ENT_QUOTES | \ENT_HTML5, $encoding);
59}
60
61/**
62 * Renders a view.
63 *
64 * @param string $path View path
65 * @param array<string,mixed> $variables Variables passed to the view
66 * @param string $instance The View instance name
67 *
68 * @return string The rendered view contents
69 */
70function view(string $path, array $variables = [], string $instance = 'default') : string
71{
72    return App::view($instance)->render($path, $variables);
73}
74
75/**
76 * Get the current URL.
77 *
78 * @return string
79 */
80function current_url() : string
81{
82    return App::request()->getUrl()->toString();
83}
84
85/**
86 * Get the current Route.
87 *
88 * @return Framework\Routing\Route
89 */
90function current_route() : Route
91{
92    return App::router()->getMatchedRoute();
93}
94
95/**
96 * Get a URL based in a Route name.
97 *
98 * @param string $name Route name
99 * @param array<mixed> $pathArgs Route path arguments
100 * @param array<mixed> $originArgs Route origin arguments
101 *
102 * @return string The Route URL
103 */
104function route_url(string $name, array $pathArgs = [], array $originArgs = []) : string
105{
106    $route = App::router()->getNamedRoute($name);
107    $matched = App::router()->getMatchedRoute();
108    if (empty($originArgs)
109        && $matched
110        && $route->getOrigin() === $matched->getOrigin()
111    ) {
112        $originArgs = App::router()->getMatchedOriginArguments();
113    }
114    return $route->getUrl($originArgs, $pathArgs);
115}
116
117/**
118 * Renders a language file line with dot notation format.
119 *
120 * e.g. home.hello matches 'home' for file and 'hello' for line.
121 *
122 * @param string $line The dot notation file line
123 * @param array<int|string,string> $args The arguments to be used in the
124 * formatted text
125 * @param string|null $locale A custom locale or null to use the current
126 *
127 * @return string|null The rendered text or null if not found
128 */
129function lang(string $line, array $args = [], string $locale = null) : ?string
130{
131    return App::language()->lang($line, $args, $locale);
132}
133
134/**
135 * Get the Session instance.
136 *
137 * @return Framework\Session\Session
138 */
139function session() : Session
140{
141    return App::session();
142}
143
144/**
145 * Get data from old redirect.
146 *
147 * @param string|null $key Set null to return all data
148 * @param bool $escape
149 *
150 * @see Framework\HTTP\Request::getRedirectData()
151 * @see Framework\HTTP\Response::redirect()
152 * @see redirect()
153 *
154 * @return mixed The old value. If $escape is true and the value is not
155 * stringable, an empty string will return
156 */
157function old(?string $key, bool $escape = true) : mixed
158{
159    App::session()->activate();
160    $data = App::request()->getRedirectData($key);
161    if ($data !== null && $escape) {
162        $data = is_scalar($data) || (is_object($data) && method_exists($data, '__toString'))
163            ? esc((string) $data)
164            : '';
165    }
166    return $data;
167}
168
169/**
170 * Tells if session has old data.
171 *
172 * @param string|null $key null to check all data or a specific key in the
173 * array simple format
174 *
175 * @see old()
176 *
177 * @return bool
178 */
179function has_old(string $key = null) : bool
180{
181    App::session()->activate();
182    return App::request()->getRedirectData($key) !== null;
183}
184
185/**
186 * Renders the AntiCSRF input.
187 *
188 * @param string $instance The antiCsrf service instance name
189 *
190 * @return string An HTML hidden input if antiCsrf service is enabled or an
191 * empty string if it is disabled
192 */
193function csrf_input(string $instance = 'default') : string
194{
195    return App::antiCsrf($instance)->input();
196}
197
198/**
199 * Set Response status as "404 Not Found" and auto set body as
200 * JSON or HTML page based on Request Content-Type header.
201 *
202 * @param array<string,mixed> $variables
203 *
204 * @return Framework\HTTP\Response
205 */
206function respond_not_found(array $variables = []) : Response
207{
208    $request = App::request();
209    $response = App::response();
210    $response->setStatus(404);
211    if ($request->isJson() || $request->negotiateAccept([
212            'text/html',
213            'application/json',
214        ]) === 'application/json') {
215        return $response->setJson([
216            'error' => [
217                'code' => 404,
218                'reason' => 'Not Found',
219            ],
220        ]);
221    }
222    $variables['title'] ??= lang('routing.error404');
223    $variables['message'] ??= lang('routing.pageNotFound');
224    return $response->setBody(
225        view('errors/404', $variables)
226    );
227}
228
229/**
230 * Sets the HTTP Redirect Response with data accessible in the next HTTP
231 * Request.
232 *
233 * @param string $location Location Header value
234 * @param array<int|string,mixed> $data Session data available on next
235 * Request
236 * @param int|null $code HTTP Redirect status code. Leave null to determine
237 * based on the current HTTP method.
238 *
239 * @see http://en.wikipedia.org/wiki/Post/Redirect/Get
240 * @see Framework\HTTP\Request::getRedirectData()
241 * @see old()
242 *
243 * @throws InvalidArgumentException for invalid Redirection code
244 *
245 * @return Framework\HTTP\Response
246 */
247function redirect(string $location, array $data = [], int $code = null) : Response
248{
249    if ($data) {
250        App::session()->activate();
251    }
252    return App::response()->redirect($location, $data, $code);
253}
254
255/**
256 * Redirect to a named route.
257 *
258 * @param array<mixed>|string $route route name as string or an array with the
259 * route name, an array with path args and other array with origin args
260 * @param array<mixed> $data Session data available on next
261 * Request
262 * @param int|null $code HTTP Redirect status code. Leave null to determine
263 * based on the current HTTP method.
264 *
265 * @see http://en.wikipedia.org/wiki/Post/Redirect/Get
266 * @see Framework\HTTP\Request::getRedirectData()
267 * @see old()
268 * @see redirect()
269 *
270 * @throws InvalidArgumentException for invalid Redirection code
271 *
272 * @return Framework\HTTP\Response
273 */
274function redirect_to(
275    array | string $route,
276    array $data = [],
277    int $code = null
278) : Response {
279    $route = (array) $route;
280    $route = route_url(...$route);
281    return redirect($route, $data, $code);
282}
283
284/**
285 * Get configs from a service.
286 *
287 * @param string $name The service name
288 * @param string $key The instance name and, optionally, with keys in the
289 * ArraySimple keys format
290 *
291 * @return mixed The key value
292 */
293function config(string $name, string $key = 'default') : mixed
294{
295    [$instance, $keys] = array_pad(explode('[', $key, 2), 2, null);
296    $config = App::config()->get($name, $instance);
297    if ($keys === null) {
298        return $config;
299    }
300    $pos = strpos($keys, ']');
301    if ($pos === false) {
302        $pos = strlen($key);
303    }
304    $parent = substr($keys, 0, $pos);
305    $keys = substr($keys, $pos + 1);
306    $key = $parent . $keys;
307    return ArraySimple::value($key, $config);
308}
309
310/**
311 * Get same Model instance.
312 *
313 * @template T of Model
314 *
315 * @param class-string<T> $class
316 *
317 * @return T
318 */
319function model(string $class) : Model
320{
321    static $models;
322    if ( ! isset($models[$class])) {
323        $models[$class] = new $class();
324    }
325    return $models[$class];
326}