Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
84.31% covered (warning)
84.31%
43 / 51
73.33% covered (warning)
73.33%
11 / 15
CRAP
0.00% covered (danger)
0.00%
0 / 1
View
84.31% covered (warning)
84.31%
43 / 51
73.33% covered (warning)
73.33%
11 / 15
34.71
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 hasData
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getData
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setData
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 removeData
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 addData
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 getText
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
5
 setModuleDynamically
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
5
 setThemeDynamically
90.91% covered (success)
90.91%
10 / 11
0.00% covered (danger)
0.00%
0 / 1
5.02
 getHtml
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getNumeric
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getPercentage
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getCurrency
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getDateTime
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 renderUserName
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2/**
3 * Jingga
4 *
5 * PHP Version 8.1
6 *
7 * @package   phpOMS\Views
8 * @copyright Dennis Eichhorn
9 * @license   OMS License 2.0
10 * @version   1.0.0
11 * @link      https://jingga.app
12 */
13declare(strict_types=1);
14
15namespace phpOMS\Views;
16
17use phpOMS\Localization\L11nManager;
18use phpOMS\Localization\Localization;
19use phpOMS\Localization\Money;
20use phpOMS\Message\RequestAbstract;
21use phpOMS\Message\ResponseAbstract;
22use phpOMS\Module\Exception\InvalidModuleException;
23use phpOMS\Module\Exception\InvalidThemeException;
24use phpOMS\Stdlib\Base\FloatInt;
25
26/**
27 * Basic view which can be used as basis for specific implementations.
28 *
29 * @package phpOMS\Views
30 * @license OMS License 2.0
31 * @link    https://jingga.app
32 * @since   1.0.0
33 */
34class View extends ViewAbstract
35{
36    /**
37     * View data.
38     *
39     * @var array<string, mixed>
40     * @since 1.0.0
41     */
42    public array $data = [];
43
44    /**
45     * View Localization.
46     *
47     * @var Localization
48     * @since 1.0.0
49     */
50    public Localization $l11n;
51
52    /**
53     * Application.
54     *
55     * @var L11nManager
56     * @since 1.0.0
57     */
58    public L11nManager $l11nManager;
59
60    /**
61     * Request.
62     *
63     * @var null|RequestAbstract
64     * @since 1.0.0
65     */
66    public ?RequestAbstract $request;
67
68    /**
69     * Request.
70     *
71     * @var null|ResponseAbstract
72     * @since 1.0.0
73     */
74    public ?ResponseAbstract $response;
75
76    /**
77     * Theme name.
78     *
79     * @var null|string
80     * @since 1.0.0
81     */
82    public ?string $theme = null;
83
84    /**
85     * Module name.
86     *
87     * @var null|string
88     * @since 1.0.0
89     */
90    public ?string $module = null;
91
92    /**
93     * Constructor.
94     *
95     * @param L11nManager      $l11n     Localization manager
96     * @param RequestAbstract  $request  Request
97     * @param ResponseAbstract $response Request
98     *
99     * @since 1.0.0
100     */
101    public function __construct(L11nManager $l11n = null, RequestAbstract $request = null, ResponseAbstract $response = null)
102    {
103        $this->l11nManager = $l11n ?? new L11nManager();
104        $this->request     = $request;
105        $this->response    = $response;
106        $this->l11n        = $response !== null ? $response->header->l11n : new Localization();
107    }
108
109    /**
110     * Check if data exists
111     *
112     * @param string $id Data Id
113     *
114     * @return bool
115     *
116     * @since 1.0.0
117     */
118    public function hasData(string $id) : bool
119    {
120        return isset($this->data[$id]);
121    }
122
123    /**
124     * Get data attached to view
125     *
126     * @param string $id Data Id
127     *
128     * @return mixed
129     *
130     * @since 1.0.0
131     */
132    public function getData(string $id) : mixed
133    {
134        return $this->data[$id] ?? null;
135    }
136
137    /**
138     * Set data of view
139     *
140     * @param string $id   Data ID
141     * @param mixed  $data Data
142     *
143     * @return void
144     *
145     * @since 1.0.0
146     */
147    public function setData(string $id, mixed $data) : void
148    {
149        $this->data[$id] = $data;
150    }
151
152    /**
153     * Remove view.
154     *
155     * @param string $id Data Id
156     *
157     * @return bool
158     *
159     * @since 1.0.0
160     */
161    public function removeData(string $id) : bool
162    {
163        if (isset($this->data[$id])) {
164            unset($this->data[$id]);
165
166            return true;
167        }
168
169        return false;
170    }
171
172    /**
173     * Add data to view
174     *
175     * @param string $id   Data ID
176     * @param mixed  $data Data
177     *
178     * @return bool
179     *
180     * @since 1.0.0
181     */
182    public function addData(string $id, mixed $data) : bool
183    {
184        if (isset($this->data[$id])) {
185            return false;
186        }
187
188        $this->data[$id] = $data;
189
190        return true;
191    }
192
193    /**
194     * Get translation.
195     *
196     * @param string $translation Text
197     * @param string $module      Module name
198     * @param string $theme       Theme name
199     *
200     * @return string
201     *
202     * @since 1.0.0
203     */
204    public function getText(string $translation, string $module = null, string $theme = null) : string
205    {
206        if ($module === null && $this->module === null) {
207            $this->setModuleDynamically();
208        }
209
210        if ($theme === null && $this->theme === null) {
211            $this->setThemeDynamically();
212        }
213
214        /** @var string $module */
215        $module = $module ?? $this->module;
216        /** @var string $theme */
217        $theme = $theme ?? $this->theme;
218
219        return $this->l11nManager->getText($this->l11n->language, $module, $theme, $translation);
220    }
221
222    /**
223     * Set the view module dynamically.
224     *
225     * Sets the view module based on the template path
226     *
227     * @return void
228     *
229     * @throws InvalidModuleException throws this exception if no data for the defined module could be found
230     *
231     * @since 1.0.0
232     */
233    private function setModuleDynamically() : void
234    {
235        $match = '/Modules/';
236
237        if (($start = \strripos($this->template, $match)) === false) {
238            $this->module = '0';
239        }
240
241        $start += \strlen($match);
242        if (\strlen($this->template) < $start) {
243            throw new InvalidModuleException($this->template);
244        }
245
246        $end = \strpos($this->template, '/', $start);
247        if ($end === false) {
248            throw new InvalidModuleException($this->template);
249        }
250
251        $this->module = \substr($this->template, $start, $end - $start);
252
253        if ($this->module === false) {
254            $this->module = '0'; // @codeCoverageIgnore
255        }
256    }
257
258    /**
259     * Set the view theme dynamically.
260     *
261     * Sets the view theme based on the template path
262     *
263     * @return void
264     *
265     * @throws InvalidThemeException throws this exception if no data for the defined theme could be found
266     *
267     * @since 1.0.0
268     */
269    private function setThemeDynamically() : void
270    {
271        $match = '/Theme/';
272
273        if (($start = \strripos($this->template, $match)) === false) {
274            $this->theme = '0';
275        }
276
277        $start += \strlen($match);
278        if (\strlen($this->template) < $start) {
279            throw new InvalidThemeException($this->template);
280        }
281
282        $end = \strpos($this->template, '/', $start);
283        if ($end === false) {
284            throw new InvalidThemeException($this->template);
285        }
286
287        $this->theme = \substr($this->template, $start, $end - $start);
288
289        if ($this->theme === false) {
290            $this->theme = '0'; // @codeCoverageIgnore
291        }
292    }
293
294    /**
295     * Get translation.
296     *
297     * @param string      $translation Text
298     * @param null|string $module      Module name
299     * @param null|string $theme       Theme name
300     *
301     * @return string
302     *
303     * @since 1.0.0
304     */
305    public function getHtml(string $translation, string $module = null, string $theme = null) : string
306    {
307        return \htmlspecialchars($this->getText($translation, $module, $theme));
308    }
309
310    /**
311     * Print a numeric value
312     *
313     * @param int|float|FloatInt $numeric Numeric value to print
314     * @param null|string        $format  Format type to use
315     *
316     * @return string
317     *
318     * @since 1.0.0
319     */
320    public function getNumeric(int | float | FloatInt $numeric, string $format = null) : string
321    {
322        return $this->l11nManager->getNumeric($this->l11n, $numeric, $format);
323    }
324
325    /**
326     * Print a percentage value
327     *
328     * @param float       $percentage Percentage value to print
329     * @param null|string $format     Format type to use
330     *
331     * @return string
332     *
333     * @since 1.0.0
334     */
335    public function getPercentage(float $percentage, string $format = null) : string
336    {
337        return $this->l11nManager->getPercentage($this->l11n, $percentage, $format);
338    }
339
340    /**
341     * Print a currency
342     *
343     * @param int|float|Money $currency Currency value to print
344     * @param null|string     $symbol   Currency name/symbol
345     * @param null|string     $format   Format type to use
346     * @param int             $divide   Divide currency by divisor
347     *
348     * @return string
349     *
350     * @since 1.0.0
351     */
352    public function getCurrency(
353        int | float | Money | FloatInt $currency,
354        string $symbol = null,
355        string $format = null,
356        int $divide = 1
357    ) : string
358    {
359        return $this->l11nManager->getCurrency($this->l11n, $currency, $symbol, $format, $divide);
360    }
361
362    /**
363     * Print a datetime
364     *
365     * @param null|\DateTimeInterface $datetime DateTime to print
366     * @param string                  $format   Format type to use
367     *
368     * @return string
369     *
370     * @since 1.0.0
371     */
372    public function getDateTime(\DateTimeInterface $datetime = null, string $format = null) : string
373    {
374        return $this->l11nManager->getDateTime($this->l11n, $datetime, $format);
375    }
376
377    /**
378     * Render user name based on format
379     *
380     * @param string $format Format used in printf
381     * @param array  $names  Names to render according to the format
382     *
383     * @return string
384     *
385     * @since 1.0.0
386     */
387    public function renderUserName(string $format, array $names) : string
388    {
389        $name = \preg_replace('/\s+/', ' ', \sprintf($format, ...$names));
390
391        return $name === null ? '' : \trim($name);
392    }
393}